From ad8ac3bd2ba45828f902a94096a7b5466209ecb1 Mon Sep 17 00:00:00 2001 From: Amonoman Date: Mon, 27 Apr 2026 17:12:43 +0200 Subject: [PATCH 1/4] Update every icon except banner --- .../drawable-hdpi/ic_launcher_foreground.png | Bin 2891 -> 3582 bytes .../drawable-mdpi/ic_launcher_foreground.png | Bin 1884 -> 2464 bytes .../drawable-xhdpi/ic_launcher_foreground.png | Bin 3716 -> 4566 bytes .../ic_launcher_foreground.png | Bin 5828 -> 6802 bytes .../ic_launcher_foreground.png | Bin 8262 -> 9343 bytes .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 954 -> 1590 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 647 -> 1106 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 1371 -> 2077 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 2015 -> 2879 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 2809 -> 3794 bytes .../Icon-App-1024x1024@1x.png | Bin 43156 -> 18082 bytes .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 429 -> 603 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 905 -> 966 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 1324 -> 1331 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 624 -> 783 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 1340 -> 1265 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 2073 -> 1928 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 905 -> 966 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 1896 -> 1704 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 2976 -> 2449 bytes .../AppIcon.appiconset/Icon-App-50x50@1x.png | Bin 1122 -> 1116 bytes .../AppIcon.appiconset/Icon-App-50x50@2x.png | Bin 2421 -> 2052 bytes .../AppIcon.appiconset/Icon-App-57x57@1x.png | Bin 1296 -> 1279 bytes .../AppIcon.appiconset/Icon-App-57x57@2x.png | Bin 2865 -> 2338 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 2976 -> 2449 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 4704 -> 3556 bytes .../AppIcon.appiconset/Icon-App-72x72@1x.png | Bin 1692 -> 1590 bytes .../AppIcon.appiconset/Icon-App-72x72@2x.png | Bin 3674 -> 2879 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 1821 -> 1616 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 3943 -> 3010 bytes .../Icon-App-83.5x83.5@2x.png | Bin 4384 -> 3352 bytes site/icon.png | Bin 72121 -> 73162 bytes 32 files changed, 0 insertions(+), 0 deletions(-) diff --git a/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png b/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png index fd5e939d530b8dcd42f25c2ef6ccbf857867be77..872d3c357a28371cdf4b1ae608a29151933bc586 100644 GIT binary patch literal 3582 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 }A?=qN~&gwE>1c}8z21PJO0mMc1 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 gPQw0Q)J<+QQTS0uqOC4*&oF literal 1884 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 literal 3716 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 E0IH<^ssI20 diff --git a/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png b/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png index 590c5cdae7a157a5984c520f929f865fbc39265d..0c3289e2bf49aefcc15a6e1f265fd337b5a7b15d 100644 GIT binary patch literal 6802 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 NC3$sFiLAxP{{pFs=CA+& literal 8262 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 diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png index ee6d88cc2c7eaaf50445c3b176cb29eeb98b86e4..ab7a9c81bed7b78aaf3b6dd598219026321f702a 100644 GIT binary patch literal 1590 zcmZXUc{mh!7{^DZOg7DoTp@-IMjMTDV{?qbj0{#q8g_^r;}}di+Nsd2t7uS;$Pp<= zuH0vOXx3RKYK&vDEM^*wFm7hY?(?+!?Eaqb@B97yp7(wKe1GxwwpJ42^5OshKmvnC zIS3QIdqMkzesTV#l`!nDUcz``u>cJrg8)Dg001Z?VF*p+ACC3?#>OI&-*~r8*q9=m z+|9pULaV|$4+sNDLSim}goh0BjN7e~9ITLl@&QFQ0I-*WL780$&z{d3^;5j47}>5< z-+H`x#3g|KNEcI#2r#d$g-J;h*VHw_BcQpRY674Dh$l;%Q5{ZNbc#WV;IzxdgUpPf zlw~h#c3}1RIQMF(_sm+QuTKComD9%+U+O@@ya%>svPc8jq#1)fRMeRCi*b=_hRLwK z|JLK<&#j2thM*e+v7g~q-#qt(FN zePDs>&E|92Xu}5!I4WR?4edMP%TX);^cdId9UKrQ`;`{`Dqz+mZ!d>Pxk&yVr{*Ik{suB$`-bcL#F|!^tA6v(k9ZFnA?7;;TwF z`Qf5f`%$MOc+U7eQ(lyQ+KKCK8Pk1`uQ$!xh80)_s1UEXp$_+&sv06c(6Tad2m*4A zgyd~9`agV@dIT!koIvWLPsRb^e+%Sj|K7YwvJhpMLmalI`3Vs&4O~T)Xq8Ve6sP&rO_1T2V~T@LaS@5!Qt1H zw+P11?}F;Q10WJ~air|AUj4vsOMRDYq^t7;d?>kY)Ue1u7BV?cX>&iJ_0jpPc5LHW z?DjOiXVDLzL+k$NqnS=kA4YDU*gEC3e*pY3h`~IM)pRUAwR1SgK1@yijua~TzoM9c zIfIzi8ifL_4(7*mJ-V_tZ>N=e(}Eod7mSU>492K?0-#MWkEZ*5q^UW!+#9!q*G%7* zrH9yFLMr^m1mj%D4M4r8ht_oPtrjIhOMVza3*Y5j90fU6w1H_iOqA2*sjKb7(krm3 z85}W{AV)&@I)O3yM3nby1hSEyH|P(^;#%;M+z%E}_lu4!qkkN;&MxVnjpr#p*<85e zlV+>q2; z(i>UX(ZxYZo-2#cx!09Aud=iSygX*g)O=0ghmR~k5JxJ$*l!V%Yr(U5O7w#xCO#~W zHqBzPq1_)t+H~LjW@YV&L^hgM5JJf88CKfJf%+W1c^PDo^u)Ri&|^wULo zX)#<3fpjLpYK9td{Yo?=(=^Kl{&tnPaPxq6PIzQnz4Gx0AEOn%_FPv?yd}4y&kOj69vEqj#F7*>D zQg9lPzzDYSBE%rDs-a>j;9*jCs-@(O=o!a2nNo38vt5y8ujwr9Q9-WHATvH6vdPs! z2mZb(-C&{M5`P-_p|0F*^!dIIb($0vYI!VfQP5w?vqN|nN1m@nh@~9!j;X}wyxQ?S z+{j$enlu$1^n)7dB161Dlvk(pf@HP@Z~6pXCGg)`HbLWdNva1VDwzJQ_zEL5`)x4= z64wxwoLF!iTR8R8&W>c)>g}ka_Vg*ikI&(7WxS~!4b4?RoqKet+sL4E>vgHo(16P` zGPxt{4CGU2tXy53iJs+^w*X7LWIufyXJfq}uQ_1-kU65r=V_`|=#xvfLZew=ZQv6Y zrEszoR&r3A1yo}!jMlran5aBb*Z8){|A#e&1W`t>IN#Q0_v6M`*rLjjZqa`N7+%>; delta 943 zcmV;g15o_747vx98Gix*007#LBoF`q1A$3IK~#90?U~DOOi>uXzwe$q(;245qYHyn zN2R6cqF4}*2$fNqfgRSFfDk;x6XQcfRkJoAbEmnDC!s{_6r%p?}5vh8FW1TFh@~F~6b3 z{Dv0$doc4PkFt3sh7i2817-zMCm%tQdyvp8u>uCgQ9y#iASAzekWh~?zoAeJV-YNg zil$!2R%3mP2ENjVF-6%GikT;?a8;3Y_L*oO%xc1Aa|35Kh!5lGQ;$_;SJbaffr)?+ z3OF=Hf|-euseenZ$C%%cDQ2uHV!KMMYi%^>Zl%I`A@j%@{`8}G)i0|UsbV|h#-=64 z>mlBda21Ga7aCiOVNSse6$8_-H-qRt(8$CXQfraf*-S!Zd5){S=F^ocr$+y;u~zSNng)H5ov7^!b8$@8%KL4V$XxzJ-Q4~Tv}v#>s!OfMm=DonenX4-4K3yp zj7J0KH}eMxe&@C0AK2b9R$G?7&`90GDhg0077amRkR7Z56Zx&K?5wrr#^KfW-{yxD zZL3s?iGS7sezna!T50ZI2?54$|9o!o{YG%@pd6)ujX&%OBht@RtcA%O_S(SJ&N?A3KRTOkBaZv;!EFE-1mM@ldD zq2R_vSQx|wVH)`QXRB48Fh=YX8}Z_kbNuXpu`XtxNg4=3!c6Ji0xh0@^emU&d3c7r z)mom|M4EDsu_?j1nJq}tk}|+#iix%_EEZuk%S5Nc>CKd|JIT_lgXo1#DkAtBI2!eplM6ElrGi$N803^F$S^jA$x;Tbp zG`^kboi80Ka=iX~pKX~@nT&^*kU4+2f>-9M5X}`+cQvtccd=+MNIKkA(_6=UO(%kl zLwZHUl)T6dj4Tr;2LyLzga#exU3tHM=FaWh?Nbtx4f&JZ&(@n>`?&M@pXYO`&+RN{ zSzvi!LUWmd1|KJn6#G1fCH48*(L%3#g&9J^>l;?R>YlmJ)nS>t$GrKjeO=ahjNQI9&3I3^@ zdwfzl*QhmBwDE7@=Hj{%+&vci&gcot1v~ll%1*em#%!?r=YH zI^dU+J!k#D4EySyrKdszLYQWLPu#|CvZFp!I&}L*-sQn}3Ia-{w`a9ib@1kkT?*JW z$5~GD=vlD|pRd_%oUOZI^DNh=+0I@J1^aa$1ijE(qxnWZuRW8m;>PJ01`j42u#;dq zBe%KtWpUoC#t&wNAroROU7vKgemv}3@O`>eWl_(S)#`YZLbzBA7wBJfz8W#3jC=iI;F%S77x9GnAx{js{J$(@}k6Lysf8 zFW=Z7s!>}eEA=pTBgdf}ZKgBR#b)f-(XnOG+pC$;0imIm7>$s<9Z-aWc|GbrBB1kpEFMpV&3s2^5x8W&SUqjdi`7rivQ106TM+C zE-+d7$g`jaSzht?mh9Ta+Uxf|SwCH3cLme#Am{w+w!$B_vY%0xKN>a9?Y7?8)B8*Q zEnK6qKKGq!Mrtnisk@)+o?JUk{vWrG>|N(yws3p&lzad3zPoSO zTD7n~y!*rcn%|O>0uR;O2gkE)S?iF!B$K6@b<#Jfs&ZfX4^y4a&E;D4E1r{K3DY`z+*j27 z)jn5suV35Fyzt`EA8}&a@2LE{qS7XMS6cd?@s?wnS2AOEdq15lzwpJcB1hF58j6lf zk~`cV-8;SD(hGa(7jM;ABzh0cZDngv)Li_p&?Di3g^EFjOa;?-#uf$rZ;2O9G=nm? Mr>mdKI;Vst0BX70VE_OC delta 633 zcmV-<0*3w42!{ob8Gix*007uvZqNV#0!~RpK~#90?bgq06G0RQ@HaENHC834wFN~I zB3eX{c&nsHK!Wt-!M{QD=)sF3hDHnat{2ZLD7|Uy{!hVmBx66n5@pcv1Vp04CY)$|&O8_!U0Dm$|hk|G-N>nDEd~E+! zE`bIPkZoHD11zHW9QE|*sEO|ey5|6b+d2{~QD`HHt7po9=&wb^>7 zbC5(t%e!E`(|<7tOEqynhaZ+r+UCmIgB*VO?Kub}im@5%az<(tlnN-t*>h%Py`~ijPnokve za}w5sA0Pl&6Z}S2O(*El9(^p$Eb~kj>Mj41TnDMQlH%d?`bey6d^oEw5`exotGv-k zkM*$gaenE!w;&P;lfLc^rMkwiP8hWI?I6)G5Tj3?iy*IKAH)T0SFJRXkOG>t`|-C8 zlDQH)9dZ><3(d#HAR&dNqRk`9E0DOCqoc{L+bKEqU0(phFhY;_!p<)3+6&J?xcG}- z9%twJ$)VlA{__uyDBmxMvAlx`>Euvjv8vs9>)=5v>4012aRQK80+3k(kXil$-^t`Q Tr=W8400000NkvXXu0mjfgl{v_ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png index 382e5cbba7c0648eaa36480951cb4a06ab79c1ae..17c3bf6f5a35427961f52f8612b2327bd4169347 100644 GIT binary patch literal 2077 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 diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png index 6f0aa29e9ea3eb4435fc5f5a7d73fadbe4ded5e3..e08875948483fe69cb185729672172981cc2cdd4 100644 GIT binary patch literal 3794 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 T&wG3QUMi92tb5^p8o*$ literal 2809 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 JS?83{1OOm#?{feE delta 414 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 delta 1316 zcmV+<1>5?w3ako{8Gix*006a~P9*>U1oKHmK~#90)mqJq97Pm=Rb4$ZJCiku$Od*% z2ti09A*>`B799L1MAVabQSg!&Jg7+A4PHFu;=!9YLGUUF@ep=5h-^?WXjF`%Ye;mX ztGn~r-Br@Br?NdW)jd^RwY%ewX1A%Sk6*v{>b+MV4D7)Gh=2Sm064yM@uxDD5C9J# z!k;)m=wLvLre&lOAdf&*r!!JQ0jBUJ0_YVV*&J0ASP!6S-j)Nj@r3|X@Eh8w4HFoW zuA)#|W{uPkQV%6$VbZ5IX&>vFOJzKI_*A6!G@wViiYD8(WS5H$a;a*Gd>rXy-n4c> zz@rH9so@7id4CmiNi(#H0<+BqO*9oR&s4HWd-&|CNo1iT{b^VIdj@HSj5FHo1(E>d ztKGkfG{xHcBI7hd=~71QTSdC!z!Q_Bhkz0BX_-TL)eGF^(U{!(PHPCvFI=}K|i zU@x2mkALWs_MSUIW~v2AC(*huX&c+{h?%_-!}>$$PedO4e;MSz~0RouI73&`@(N%aN zvAzXU?xaJUN;-J=y1IOE#mMf*4Fk+o0u7LxZ+|(|XXMsA_6jVH;S-qn=OFgecAkD8 zPy*(1itG(Ul6(BW5W74_=|4Go&|hqXAAHSM*N1+h^;!1nlfmUx+ij5kNbX2OD@?XK zLQ44ZZx4#;!W_GJ4mx2{!2x*U@+jC(M{%zizO%xX8tnX;Q1tz%SD*DRo`WC?msZ8^ zTYnB1_iZ{HSe@H|-`)%^E&G?AVi(Q=7eI;kdL24pxU|X*^Qugg;+3J}gh~9g87!{& zOAS(s_g6$KaLHKifg$PCmV#LiZ!U;C+u?`b4Dk-%US^ApxZ}$T#@@3h=;@i@t!qU{ zqZuRVV0g1k6Xc0U;Gwdj=i8gT3s;E%aerT7cJ%neal-;)lEGV=hoim19c;ASCpD6! zB;D@Y_~asWG9gI|d^IKAjmUIKquwB@J_M1mZTLF|obnzmW6S0@|D=sa>HHD?)jiE7 zL{E})0#<6E>o$KF_oKKY)1w-Nj198f}!9rm6%9)Ek4 zkG?fvOwqZe!rvh2p4s-PSl+oT``VNhXFwytrKvuf$pXJ| zxA*dAbYYIn9vI_-M7;8iuCK$tCR0ZhtyK}x)VNy#|GemiVm`_kLq?2FMLN0v!W>+I zv4W4Ltm2T7d-U)tVgGphIR7%VoqwG$b+Kjk0rf7TED2;FH_Hpq13Ed5(Z>|(x%kzI zWWT+sbqB)%wC|Sa<^;^#R$=Vx)<^Lf?J&WJWVn&ca2_Tm0Q zrVXjT>mmaz-#)aR>+VwZYL2v5GJ0}+QrV1YANeYpdxe}%IaP7IqH&;Tr z_FT1GvouI6W08ofVRM0x$#tjmw_^ir^B3h6THCzH$FVaXB#Rba+q-bl zucG!B3#O_0?0Ro<%Wm1$xR;-{Uek8DeZ09jZpu=v^?RNw6wTC|UslgkdPCusrLUYi z@0D5l6XItcl_+O4I2QX!dvnC+R+Gd_j(^V@)jyorzuTa33-6s)&O^%b^_y!p23S5; z^fF_(nAiL2bM7KH?%W$HSzB(M-~INTsH(K(<^Sc&8P6x(=kqM`f2Zi4J1aV#F(CdG z%f9{-FJ#ufy>!dshi?47-p?C6{=T&{dAvOMz1t<$S;JJ!9d*u8Wu13*KKuA=;8D}tYP7@^}%}1 z(#+paQ*GxRs4fvs%-TDZDMTQs_ABH6qxb$hUUb+!+gV!P?ZwBl>!Ji7{^NZ3+qpyE k!|4UX8!p*x>y^H!E4u88es<>mEl{%bboFyt=akR{0B?av;s5{u delta 610 zcmV-o0-gPj2Ji%s8Gix*007zX@K^u<0yjxSK~#90m6p#-RY4TT&-}OvnJ!8aN~1-Q zJffgQiJ%Czb=e|<{sCzv)yn^%bQkm&v~N*Sx=3-M!o$@=lpe2iBba7Eyxz>66Zg&? z?|pB6+}q)CnRjN+XTE1h(EiHd+4S(~{EiOq#*+i#F!-&%f zyjprQOXpPo2?axB05lIbT#`P6P*NMh#$gGXWD8=r71or`-Vw8(N>oPx z;*`rvpmcInt?p&*sZq7E=N&3Cn&+H#8+$mW*7mdZqgse-jE7f%&0LG$f9>@WU^Ca_ zcf0PG^my=W$A3IVr?14z->lgveQ?#w&GcfR7jWkYg!Mit#O)uUFeuY2-)uR&3gzp1 zD2!Cww`7S`mRC4^GcoW$P0ELTZ079`&EVKmZuKft-DtIS08T+FM?1s~o(Bzz7VgWJ z>&bqM3`EbzJ9En*f@mx|4~a=77dH5_dt|ULdND2*H-Af@T1-GMxqOZc_pAB=woc@$ zjcD$!Slqb}VPNW{M=~}J$&;JxaSf0EcI7d6hTa+$ZyP?bg}Sb*eUAd6#QX}K z7$M!N@mr`Z=D$GO(pw-HE~Y;l{YR>$mOI}bqlVMP)t4J}p{rQvKGtZG-%(Yq{QE&C wBK+SwHqkT8Qy=tDX8w{i6}?cT;Pg-Y4?f)0$GC&R(*OVf07*qoM6N<$g0)OD761SM diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png index 85b3cd51195750bc2a899067a92136ef90f7d21d..179ed04ca492732af3f5d4d8590e4e5791293544 100644 GIT binary patch delta 1256 zcmV0Mx!%gKe*EL$_6^JlRL)jR^qicS?9y@3w91 zwh9%NhA?E~*=%nxMY~1~fq23Id5DdqVPl9$sYEI?F*iy6!YszQk)}4rxe@XgW=YIV zMoA@Fq2@%6_-Kasc($ocd^}5hG=n*ji;_yjp(`36aeqiu3US{qN`xv@j9JJR@{kDN z)`}rt%p=CE@Q4wn&={oWPLcoM4EpS`mfh$t9V7q#8Pap7LY;>2TuFh}pFqo)$iCKR zLuO61jEUBt02y99hrdPxbBkuNJ~p*(SLj1S0lvEb-75BfyYDL`gjaBSips*Dcohd{ zb+ctR&VQ{|HnrJ*XD=0^q!O)A_wok!E^i!a z<1d!Ex40A~4WOGxI}cr2LoIin_Zho&62BJ$Zx*+}b zNvvo3*_q6-eeuWt+GHfDccw_q3{zYAi>(`1U^kj}ZFE9Cg}YTl=#tdTFkaE7e!F<^ zS^&wnCP~c z7k?;!ya-!PBvhdB$41)Pm(yAL-aCysmccFUA`?0QUe!U%#Ie^(Y+byDw@^I7Q!qrn z>v{`CO7kl?8x^#yiHJ)?T%u)7oQ(>l`IX2;LKvZ&mS|@Oz)+|rq38dygDZW^@hq{u z=8{*kDSm#F`d7^h{@Bw=b{Tkg*#{IpK| zL4o1Q)9I*4Yv5!GDK^ z_-89o1yA*Xh5A(oT&`1TJRgf;OS-CWBZe3lHQ_Y3K^=G<1b_8oEL|4Sxe#3ts&^ Sdx(z!0000({( ziVKQfG{%5YbP0+V(UT$wIf+Qri{wv8#6yfCB8cG0fHwtEL=-(_V}iR8F>{EZYrtgO zHQC+FSASJWznb+O*y($JJp?~3v08so_;LE|!2EKMTPxPDCyVJ8e`g8s)2qEl$@&Z8-qF;?n$R6a}#Gy<6-CMd(^MvkEMe z-~fI6VB&jn9x@&b%a>Zar_vLv&3Hv1LFB@PrvrTnLn5qc25D9HL80t$(rF!TYsG=A4(m8nJuhy(js( zHSdEZ#rwt|HKJ>k&0Wa0I^0?Zje-}KaC-w*3+7!T=wCC5h;AjOHaSEyhIA!zi*#5T z**+uw+LXMU>}ZH9edrahC~VBM+>oxJXbJ3vd+9TGI|sjnO^J+Er~11G_zxGn_kS)d zNuXj~<$vNQ#EecT0aKH@m{HI*sHsXsk6`MSFl9IaSr)`E<#3yfviT4qc&P!$u|dJD z1pnI=xaz=~T%788{)wh&uBpq@ft3ki;{0Xr{AKy;{m+`}!cf7b=uR5}-KuJvO$T~z ziZ!p>(5}pvE2Hau{0n^gTavDG=o@e=s`q_hWq-4WpD3U_qKY zg*HMZ(5^4-YS&jFBNfuU$%Kvcyo}76VqLn)n1g_BnL=s{v!?OU(|qx7@A#QmEF+-5 zzK1-z(|P4cn=Pc%K;2$0s-%zv1{&i}f#RtLD6{!6yOwa4$!P6Rig=nPZL^^YzZ zkm|{f!U#_aWau@-xJ9^!iwY~a;3>esJZ9WvO5_YqbcV(*x8;-r3*)QA9)Mhm6K>tt z(4?wuN*JgM>141-Mi$;QByE@5Hzv^$qzN7!3ek(k>1Z2HLEaXsQ}qOjYAwFIWoisn q#UisFKHEm|kpzAS%K=i>mi_}S5VDPzHfXy50000=9+{6 diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png index e34cd6435a1b5eed81a9065a901aa9cc1d546489..b4b3e9a6dfc6473f501f9d7c5d17f2b47a57a483 100644 GIT binary patch literal 1928 zcmZuy3pms79^d3LCbz$#B4qvhlM=b_a<*DB$5z9%O$xxKj%45=Q-#3zt8i2KcDyce&6SL-_Q5^zF#KR*>StvPB{<= zv>kH{?Ft}%YeJ*}d!hY(2msjg9vGsN6UYGI5RfDo1d;?80DuAimnR4P;x=HFUwkVJ z2)qXD+`@ma08{PcDF8q+9Erg}fDsD`NZIm-yE-C46#~sk5J*~tLEGbKxih)GVVd|I zm-51ik;Iwxh2OW=xgjDoA?}7=x9bdG2^?e!A{5)bODX4p+z%(CH@2)#lad>E&or z21r%L|}|F~vMF(Zryzi97C$tqLU!|ORzI9&Bf*WAS=G_r(ax-V#cm2Q(edA+g` zmubS{h3v`^=j(wt%W#ZYsu0QR4bpmDT~B!!E;TRI;&I;#hNA>#)5$Tx z)VZg#nK36ZH+;ZFW1ThE5Hl)~_WMY7*_q9A=O4MSi{p5$BC>X2a7)2kbmyMc=lTQl z+d>AH6d#spqcl*>zL^?aLWeG6S@WBRGRgAIBJac!!^^6ceQ{Ei8u#stUHm}B zrm;0lY;s2d)%9G!ksabW(kqgE}=UN0Y@c7a8Bk>yCos&$T3(eZAb2@~+ z&RwKF=I2~-?@p2}LA&EcCTg-cXn+a6BR){GQA5oyE^xS`rh0~Ub)!ut=q4G*pl^Gu zDz;N!&Dk$=*m7LXBTAjxn4)nkh^Z&-T7T|nd1KmPFFOTd?F*}vC7zV7%_d)HI)9U3 zbfexV^yDqD(|^PTP64Q{!XLoS46djfKuiZ^G&mFMx~b}|4F1_^%JU@ahu*NYD4S%tK_S5_Bh$${I!u9V!>uS!;u2;!e)0(L*?YYBgCVp; zHbvKbokmh$4&@ z=FhikJqR1H^5Quho6oVj6a8of2`}-s+*10lD+^9a`u!>Wi@YF71-PE1g{Z-%{Au)x z>9?2N5jGW{H_z`iPS%7wRICMObg`PRgqypNs-1hHwKw0DCD+yPXCcMvyW4gMIz9UW z9xhru<1UpcDDE*HOlao<>BzcPe=jQaoiEb)i$aLrzHrq%ULxX-V^M(6X1w+K>@eIR z^pJP)>^GIyP&-JMpZ+NtJ$MMA`?3l(ysY1)enLm`H8imC3Hp$Cy4CzT<|^>UzT!zi zg7SQcq$>FBNqY+ZV0@#Ilm07TE}9svbQXil4-}MR`PNWx*(e=}s!Mr`)=XdX*naX| z%aLt;#iq}0SfV^}h^IRa?D2*6$evUDVFnD3*w^#*xLaXS= zbeJOLYa44=-Z3t;zQ?{^ZO{3FMo^DJ=6eor!cbJ&XnlEekz(XV*Ll@Ip+dx=!)Z#? z!o=ws<&31N!U}s2mFjsc(~o>=b;haD)^NdOFb?CKx9X`{JCsWr@w#2L{OOTX-}k826Ym!M~=T9fE_ccG3nDTWG{E#XbG*~?Lr_f(b_o(S% z8sm04p5*^H#-32lax=2U3?s%d>9C-;gO^u^T>|OKwNxkto{Vs}Bf47Niz@G2%jz#n zGM}nspvj>*{$(h@{^+%}o{NHt0(X;4S^q35jCD{rlfP!$(Yyw8rGI%ZIm-_h9fa5p zlk z0WNg(E6JSJ>Mm3@5b|U0`-OI+mOEvX>(FXl4JaEW&tu}<(FJQtJwirVDV_Ufd&1km zXGU6S3!{@0Re6>eXjgU#)n$*Y`3KJV^$*$oVYgI!BAf3vrUaj50#Pa{I!SMl3rKRMvj=ofDs zow6A7$Y@KgR5>f_`c5G`CQ_bSo&t#XcQm^Y#?gU?&}zqfOZjH3pQ6I%5_7TMhKY+i z!AZfNHCjzM*e7x13T!Dg|6}WBM7Dp0TFk6TmRW6ff@eX{|M!VsTK00`h=PoW1=R+2OoUXC*j3F;8n$_4;Ud)AJi9PqCS`yLktiV0u`l*#sGpy zDH0)RY3avZySp=H|9gAAqqleaF|&8q`pcyp?cVIn@BZ`uo_}Q!4|ISO009vAUjbkN zFn~#XqXDD=H~__K0N-$c0)P?6?3lu_lel<}uW5iW?<*~UagRSOK*}^6WKz{k6|kTI zB=DP`^j@2Plvd6Fa_(=e$ir4bDhnBuLd{;A|QN0dNNwkwv)(t1rT` zkj9VJQ2TMhL}AQ^w`v>GeahdMqSS5L!zL$}xpHLa*~J)?|S;lT#2Hljz-OwS20YEVRX85lDMd zjK~NEY9Np1^R}mf_hum%#{CEi?b%-K#ryc-zs(QMs=d>2ElugcLJDn+de?}o>n4Pd z-cIFra(}i)rh42}jRk_gmKVL(2Z`k_mb640&`b zE5GJhEF>@#QG_(po>?u<5Ak!iye^?@7m*E%`8Suv9e>_$P*jVuDp^>d)<@KySgGw; z3kIKl;cKv}>t42dGg+826U_SLH&yePwJ$1J5PymjXDLPq5xNG9g5}}Fv?cAI2~FiZ zFdtKfG8VD{LeZAT1SW4?pRkMt8hZw{u_#-t%Yr5+rQJ#>%!HdR3%dO6Ksr)v&sG@9 z8*l#vJiH&uUfw`fcbM;<7B{52G3HE?(q$nnp9T8hJ@j7NH73h);#yeqo7L%)D+v(# z$bT~Hiwlyc=;}yhA*IGZCTQ^ujo=~MpT&=B3f%0mq6dX6Xi;lBEesR~WT83J>hS=* z7G#0fy>{-xvRwpbTL(1`ySRnQh6@;raD##cc@xqM6y>8#JBDVqYE+Y1I$*6>C@znH;kiijqhIN!9)9>reA6P1rIg-}<^gfL zV7_y@_BH(206#Vm(}W!4fwg2ARxWJqIIl7*9$>Z0nDYe8&MeH;8jMfa+HpRIQhyH8 zz<~O=Zf?W&n&PRHHZw@fzGHKO#JeL%XG#zP^#X0unF2y0#pA)I6=O6-I#T-kkAp_c z{eArGO|Q%7{oQQe!}bAySC5FBF{MLUZ0&Aj?ve<)uAA*GQeYnH4`0Fv1Xjw#$7Lq0 zMGdPEjtRFDo4u0(8saXT?fx)+o-f_1Rp zIvN)RE|3MQde4(0SjPv20Jh5}0y^!3U?iIW%vPI+VyA$We&qdjP0Ip5F@I#I8+1Nl_PgQ$TvMZ0AOC zVc7gwYMYN%T)4|ZKEP=LKcG*qBJC;dvE`-*3#F~0?O4OVx@diWt=4GUmeJlW(c1-| z{vM?XpmNZeGQG=!g{@r@i+@zbQN7sK(6Q zy*smFN>h#p#V-zPOEUC<#gI)n4q|vPN+0UMvaO8mN@on@%pLB&$bUIR5S@i%1KOj@ zNk&h+wt4#XlQ0!F&#e*tl?|{YV~;5%xJXwK_G{_9QNcY|(OO4ct(vfB0)WM#W82$yb4aIVwUJN~P&;C-4R5R3yq>+Rd=!(Lh+ zWhild%y{FZ1f}3nj(_yXHBHE{nt9XM_al40mp-+MTh;{QNw8d6BH@AdAvL6B%N3TKqW-1sTz9Tdm?#&Aj7?q!SY+7Z-*a z>4hZGLGX5qYqDVBCq2%cwG7f|9cwP3UJlx698b1v$2`ON*MIW^ePrSd_d!W5M+Czi z?ZXxJSC1G&Mirl`k__j`G5(?f-&GmIecD=V^@}J-276#cklIuMiRi>L%gu~7+`K_F zdcX>ug(uf@c#kH`$58jgXu!DzuTu(?G)G@H)uS0w;g7_It=5j=M>FV+H2g#|b(5xH zGb0gd)+WAk?tgRK8ig#ze$k{da^-%^or;Lv#mu1?PDn@*O6p&5WT73UqoBCh=g7a~ z6iez+l-kAN9AePXRtW{Vvkis6Ab$fY=w&fneVKFPF*Aa6Xh^H(dO>O;Qka?_ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png index fd15ff97b287ebb58ea20ed1b8c8d56aad213c72..dbcabffb4f102fe5a76748928159c445b0fc2efa 100644 GIT binary patch delta 955 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 literal 2976 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 delta 1112 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 Mw2gyx1Hv!$Ut!#xyZ`_I literal 2421 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 delta 1288 zcmV+j1^4>@36Khq8Gix*000Ae6w&|y1lLJKK~#90)tX;u97P<*=g-dGUGJJ|YeQ06 z6Yy#awyB_qM(jiLD2?EIAB2KWzPBPih~SIR_ge6w4}y8JV5uk)gcABvYAL2*`bTOt zri8XO$?e|m%*y`ew%NViy_=cctn|wgZs#U5pZ(2mW`4ifvwu)QG5BjC6htK)ArDF{ zxW#aF5uymWg}SoL5pBS7;MPl3@Zst!sRK6$^eR{zU_vL0Z>XH;LmeT4P$xDT3esV1 zf^9l_c-fNB3@&A2HC6`Rwq6J?Lmpf;U{&x61=a?%=nSuDC>IP-AKt}4R*cZLEih+V z9Iht#gUGFw&wnbPwF1zmT8A-ob8&N6DST5Us8k0=(#ginVXfGrac$&UH;c6z9~~fR z-PBr^Vr{^a6%c-LgnfS0`(hzjTs3b+S~t0tCs~=%Zj=-rZ{d*|9&Z(5m1|jYAxpB- ztz1WSCBO&prYFQ$n}ysIPwWVm*O1E-rHtUc+k-og4SyST!Eqy>jIsK8|DmAmHaj!J z=WguecvxLMx_|z~3}&#F}zsJuwo-P4(1F-DV+-5u@fUcQ@HgCW8kX;`)k8Wqe>KnS74_a9g_@7h*A2vf74A75(6Fu=G%ol^$=C>Afbb zGK%Wi)qi+a5iDC#>knh1UOOos;##2t6ieBvkQX{?vAMPGp$=E52iow0jRLzOK~_j> z+Cul#JnE=Akbmx1^zBWg_c8CoeL91dCYb1|)k{I9QP6yJU9pMSkpMhD?$YndI_{4cUfiLa}X6~@=k z1grf_#930|`f9pGSFdvHN~T81(S700kIn+%i_XPde^1zzC_;H=bf5228O&=-wp7=UAUX^ zl>Ye}{Kq@Naz?y@+t1^JyW&SFO0s)w(SMpn^gstI6VzN*?`=ZF+9~JYt$fAa;69Gm^^B*ep$?IeI@NV(TJq^%g zjvZqS;IdS=27hjmyfIEcdW{^L2<|@#@~suVksWK|sZsRE;pcCtf>P8M1194E4}T82 z6t}T8uX}o)oqQF)xd$J5VMt|xf~(8^*=vD%I?4K%rc~_GNgR0&(IeOU@=AC}I8qz3 zp{#lRVa;GQGS<+nILpyI;`D@G@;y`ym^LY=%3>AZ(FIRrja36z%-TwG;#ZOJYM~0r zcfUE#k{@e}#F=sxl0%;#oD9qh1b^7Io;xU|iNMf1L*j&CIZP1YT3dGXX~PPvFnVhF zmJ_|7_YHrlk}VpUQ|;_WIX4Vo(Z%3ZqOgtaXrOcs4mI=GAR4S%^ot;^a7X^YMLw$j y_?YJ0(ykc##|!=002ovPDHLkU;%;z?2AYM diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png index c4298a3b6192a22c566e98cef2f6d04d456c4d25..6f1043d7875093515689dbfd81f36a72fee51651 100644 GIT binary patch literal 2338 zcmZ`*dpHyP9v>!)qs(PWgf%a7PYt9n+@k|Y2C zkj7$Au0o9Xc0qfD@zz9of)HFTd0~Sb9Rb=x4gvs0005wn2|*Y{{=+jvzjG@Q+3);Y znXvG-@Z~rBmlB3LNAG?i03(rDcaZRqKo_rn%SXDNMgpGpE6)jg5wR#+_t-*C(HKGb z%)z##VEuSrb<54qrqPVhJ54X>8Zb3=+OO0qcXu>Wrb<%5gTd|k(Do>~LyqB&LD{{) zVjNv`P$#jll29={;}Z1ezY@oV)acv z;iW<{QzovuM9eVkZ>Oj~9iS8hpztZ+%Dq1jAAu@1_T;_Q2^;{+KJM{+b9Hqs=1lHx zy9lFQSxc+V z#)wS@(;?yL6p&f6Bl-eG;4f@0Gf_PV&Y4ETr|CxQp$9pmqL-bO(ro;6uBpDMHZFkb zVOr?-Uzd!=8U|TU1Fv>b$ zJ4%`6sqUC{%7NjO+p?d$rZWwIve_~G6O8+jpgtzY>55S2t~C2Ta>B0ipO^QSp=xovx%MZ9DC1s{Ne7g;L+(^xQ1q^4rP4*|6mp zrD@_d=5mE?W*xM&@T~Nc?!QK0Nr1S6omnVXYC%n@HjftW&BfKz5jjV65+mbtKBry# zJ*dVRIq}%V!sPiH%Z6KilwC9E4|(k4k)@iW?t4hRB=%=zgkPv_M~&ufD8(>-#prJ9 zM3_}a{k=F``*`J(*ZvPetU8XESd2QV7eGZw=?r&YfoW7a!}qMfR79>PlKKhvUq<{` zq=o1gND*)GZd;6}>UzQjTq!ndVKig32)Z+`uWQgXUeQ>YU<&Rre@&4ORqY+ve81YU zWsz4)jJFI*T<@u}S3nbP+-z(`ZLy-$j$js-rgqnqdD-vuvKUdQ2t0k;$kfMG4alTp zR2SRhSJh(IdkKCW_1sAd%ocG!lWqel)Ppam7H4!@{X37zd+AUt)ku)bMktTP{&aCpRIUqF<_==I5Y zOm4zwK%Es~F1YG!MQmc>3ZWLY^#m67vD;RnE@)O^kP9zVIXGTyoTgy|S_qTXlDg3Hhp)%iCLcH0dNg9aoJ$>A=p36);yO1ezA<8vs{|z`(YVPAcH2&G5bKsn!E(?sU~TKPE+wg#8XM&d7pN6qCL9+d0X%Bl z_^Vp9JA#bfQTtxBzU_Pmpy$F4i<+*iw`aE$M5-CwjnFLmkaTT$-8j#n&RkYLG2#k` zw%uvg>2`8hW$%Fh$V-^ppvFvgBmAW~uyac?)j~vyv9zcexo|M;0)_PQ;dZ(tPs;7G zYNQy5gpZ1e`WQ`XlfC}Hk#q*)`c=x_(Y@!9QdvZ(?2LV$L8Wg2ED0a7V+k05aLX6%r3EbVH!^-X4|%^5cEHxK z<)SJymZHa3WxZX^v${1qI)~p&!Xy577XS@h@y|XUOtDrcQmW1p#&vvcnjln9oOI&| zb>Y+vDXq$T9$G;3N9d)}td)mSPdv>s2FD-)DCe&Ea39YG8i}m?TH*jMwGI8A0Li7z zZdDYKDW+_>enJa4*OZ-F6q=Ps)8>(-L*(k6L>1wud)*20_xg`9>0G}_?81f<26))- z4iznAjM{(7EHc@(C0EOn`Opn3%l2COR@TY~P0Zp~CjK&MU}Ew3IYKLoZ@=T-Eyb2v z%~2`@$UJZB?;!JnuP^lm#=n`j@P6vL8!I}S`Zk)0BMtuij$3}DAt79X8TUmi4_`^t zmVrn$RnJ7WtZ2|$C)EV+oL!E!Ynr#KQUIG~>24>z_TFb_PxSR}#BDssfm{a7>K{$e zmKTXyFr?Q_mN-h^c54F=?$7C*vqSWw&tG|LGdRETM=(6W!gh1bgB=oUhRv{p0U%xy zqvY1#9T)QV74`!?nQ<5KW5=mTPZGaUa_}DqXRVvFmHRWFW?$SuS>4c6(^t9_XzNlz zc@4}9v()>f7#gdM{G~L=YyS_H_5WYve`N&X`$MP$`NO_--##f=v=iznG9cwo&NVmV literal 2865 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 literal 2976 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 WB5g3A3laYS0000gJXqnD`B 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 delta 1687 zcmV;I259-V44e&+8Gix*007#LBoF`q24hJ?K~#90-CNC%6h#z&)m8JkGt44;V3pu5 z7|;dxu;@YJM~q-lF9r{gXpCV`M&chJ9yIad&8r@a5kpA)AOR8+^@8HE2Vw}>T@Xe> zfJKOWEc-RnGhJ1tUr+DO%y!Robyx39`0b?A+uil?>w53itAAIo8EA(AzyMMJ!oA|0 zl<}AXAoz>~pm;R#0j{|hOj}JocoYCtfC~ORQTFLY#$~&xr#q2}Q5*ygP^uduRap3Q zkVSgcx+|d&f!GoyP4Bn{r?w@Pb-V6@OwB6<{HPE`@_LdWu6Am{=Ok^y&HQ!ROQVI=t5*b%QeTCRALql(ix|aDM03@eL4P3m#V*JuDhGc7Q{M{{*iY&) z242K6s9JiFqOh?5L_$6ZW=UtwCfb78=F2OHW#aSzW5O&zu6Zz6^MA6t4Tc{oxYRJ0 zFEkR%=yaM$e~t_k#F=T&yT-Hq^7ewfwnB!&p@L?-4t2z61LNpm2S>=ZKI`M(#l)Sa zTVzvj;(yR{;@VvG!(X+vR{=;Y`+ar9Qo6M(#Rw2cCp>QDl4vNY&gLvM{msOv))}T# zsG$3g;TD&e_TlOXs%Rzv*R80XkQnU} z4zV8eO(Mg1bgRC z4;MWjpl@!Wd$&|S82Ka1v3M3^q<@fxe3HIB1dQ;b7n<&}y<3fUx7&|Bl8!|c zY+upBWwhSC6##MWHvjrOJ21@lJp~EMzpsrO0igSbjD1gnl;ZHD7{95xVW@2tp(v*G z6SLpii6W6eM~B(oEhJNmL9qQp~IxjI7+CQ0y+16N+P_!$1hGRHXQJ!a8;-#wO9uBeBJx$rCNW-X9^4<*J{Y zuC2A|z)uzl0pq~1t$T+jy;%`Re6$lRQT=>8Qa@Y|QQL?aJ0B(aBw3e{w>`FmtrJCK z#*WyC(>J`!_auyG`t6vcK|n;Xlz&Dos_hc13+KlK@yGQTYj;p*-d$oArn|&RY&DQw z-i)pj#l=`AG&Mw-t&IjVQAdmi%2D#gd{z4ESX+=@bU{ojyn`u2#CorWo;yyWv!8zgV_PzuR^3_EKloAYy4nEOSKoQWWi({uc*bL@i=x_glB9+YJ(XxK1gZ&gc> z;>c9E(e~6JuX)UD&Y1`Z{@YbKQ)cgMCj$kNR+u5>^qh5MO8n_F#kEGlJ7(k6d=>XN zc^C^R&fcn=y#;+4vMv+U1%G`|k^hEGaiShV=)mW_E}*iBusfZ1N~+t?VL)1?efbMa zSY=y16af5V@pm4g7b%URTvyCW-(qFx0M zqF_dO(M?n8te2cb;~q@y1J5=K__hiF`ZkKykVlOO?1!o>G&!=$;eXI+O#p0z1pXAg zbM+PUy!&~O8UGr`aD7+xutliQK*h~^?7@>bLas~Su!NmRBg8zM&v=sV7iFFwUxq*m zmAUGXn5~4!!5qg<8g5E1$1AF9MS?GgVm&223alwgCF<0Gp_DqG$V%+Az`icAP2$XP!1FAY7P7v+mc%}dV002ovPDHLkV1oS(L=OM} diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png index b656689ecfaeada71e9bb78216a865e150e3a38e..f96e994ea661aa1f005ddb6cbdfed6a64dd088f2 100644 GIT binary patch literal 2879 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 diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png index b18d03d111298ecdf20a78db7974452220ea650d..cb2efa409de975a69d28a92a2ccb3861caa2ef60 100644 GIT binary patch literal 1616 zcmZWpdpOg382`y)hEq0|9&D+pBf?ys$ZZkXF!v*u8nY6yW5o_mgj|NG2+*u@aLQBz|gY$PaBL0sW!3%EW~^;?6Dp z_a=r~oKKgSKoK@rPq6sJf={Jw`YZr!FT&bddq&geX~W@)zo{p1jf~7$ zjid9V+bMf>A1Ln83`8X-+rn^}8micyg0;zFp+7;WW?<`v&DLWF$8$%A_0!+z#g@rQKKVy4MJhxilL+<4f{05@yZ?%N^j#)n5Seafd4Z5joSp z>LTeV9i^i!)l(DEJb{Ii^cBM18KD1;WD4a_A_YtI&sm5PCFLq|=sYg|1b6c-XmoKw zdXE;J#^bVIm172=1*qxPdCed%**k16V%mHFgr+#U*H zTMfF*txIH}yWA}+(Cs8Xyq^-CHptP9gd=Rqd{@!X1EILl`N#YfS(rxk)YCV{UeXKs z0g#rCB;Hw38BB}PSe#`zE88xTbHUE+J=AI zdaM1Bov$BsKsdy$+%%w?+@fZ@V`u1CHS`e=X042!^b0%L^sz^oaGv8#{8nGYK2v2J zROS2OqT+Z-4Tju2xs-rhgfsj`rr++WD})&<490qs4DILpp%`qOVIyJv&;5?`UJ}VA z_32A&u#@*h#o>j6xkoO=lO~4xp6f4fPwqYBp%64CPuO!ozud(7?X9bQCD)(b4XYA#)SYuHug?2JGHl zKJBf_8n)iGZF3CIk%W11OtCSs60?rgo|&BM-2^y1`YxRkS+agIUT)U0V?s7p-&tzs zIT&mWbuB+a{%jOvuyKR=C9q)hP=CNSuIDrCs^pmSG)8?C8yTgqLH?CBY1N=UV}6?4 z3>-dw2VW_in{9J4^Zx4AS(a4DR7fZ!GpUUVsn&4}jEHqkm8%C*(%Z>I0r70i=t_F| z21=;VStf6ju(JTGMgTuKk=I(oaNqdj&Yx6^|C`DY@hM9;48;%d zT9!vQTv=(b?HF^%4xy3*Q_y=;I;(SUpvILZR^xi9(vg1440N}$#JE^*YOE^Rq>R4%Y;I_lgf`E#13~bW6f4kQ}f66uqz5P`vfQ ziv3T&evs)wR*%5XFPm(OKD}20Cjjb!b3Pf z(WVIsmv`b;t=TS)5%*s9TXqnabM;1gM4bx3vGnva#K8RU>$ZLyM4>jv3Ab344Z&U{{Z27+nxXb delta 1817 zcmV+!2j=+D44n>;8Gix*002n!#(V$(2INUZK~#90V5IP{3f2CmxJ%zGzH*Fd;EwBJqQdqJUcDBasjJ zriHY-ZMWT-x!25@UAkpwcIV^Xx~;!Vn(ge)-21!t+}}O-+$A1|*R*iCYSw znBXQ3ar2O+X3}cTBtT-4CiTELHGEk@g(?`MzesF#fI_01B_Nrh;*)e;Q-~^C&y2@o z-X<1FgOsC027fj)C{csvXpl!2r|2YeWR|0muS*Jt3N--ok*zlhNl*R8$cp|eSajq_ zpY5<0g(jJzW9B>17o(&IEMO|CLpYK-YQGj?=wX~pw>^~dg&=9h9PY|t(Y%#uuDZ=g znsl$w)<4s5NCnL%K0`y2o@BQyLk~6yVMECcCTUcqtbg_4kj170UF86hI%(;c{QBU6 zEa)h%$rdrYs{ekjWdO;H8h_1X#@-jf(5%qw?sY|@X4GAV5%k{LOR)LP)OE|_%0#S#B+A8g0Y zG#?m}S$|}KMqG_*PA93!UOjcDjUDU25#?9+!+70v#5Cih=P|>|8xDvo3Kym|B-<1i zs>wh{&^2gdJ|x?8%oy%!*fb=UJ2uVTt09>R>(tKo22mLIvQws_k1V{>YG6#%J;5v>a5My}?1 zXR=Bf%^ZfQdJ1U;i2s8#paH85_{>$PRex8lcb?X_*#7b~j0NlgnvA{j1V4724-6`+ z1TdLtRCUl_?$wqrz^A&ctv|u=y-p9dr;M#@@R3ej*2UjCsHzg~m~<-99u;uDI)jhT z)0T9kwy!O3`LXsZ%%_a^*Wl7lQFfd!PAe(igB#b^^?!$-j#(%ao z_TFE;^O#}my=Ps9Hg`Vm=O=F}DIO}@&()BmGVqf(tsRHird3)`hp}mu7s6w2uEhBn zw-0w8b&g$ESUgcvPSWLh;->wC{_OX}6{_?coBAJIBb}%!9;$SzB**XFomB*JLwy+@GE=Lo8joj=`G0q8YM-u# zWU+aYS}3%_vSxb9|8(CKX>i6Cp`xp0cz-s;PXkdI(m}p%NKDUinNBj<$mD+wX&`WU zH!jS^9DD64t#7gWxXhW`F)IN+`AT)Gka9I&DLUMh#Ki(G=`gmfwfFQ#ttj^7jg2cY zpBA@E{BYbT9`i{ftRrbrOMk>O76I0teq+~j*qt$Ut`mbLF&gxU&%Hp?aQz`fHyHcL zF_?@yW->lGp?>ShV%W6jdWe4t)}|lW>npIg3%fJeor!R*5aQB^z3;d<8+TVLAiX1C z(L52~(G!=8*4qbhX(#s1tE3Bt09ss}yOU@rrfJUb+Ykt-ghetNcYkoYR4)vR3u=S^ z)1VQR0I>^h=?OAgNp7uL=v39MvR~ffPWF{(hmAC!)T|*jqZxY+9t4wVXo$JT-8X?AdH*km|!o&^Aa

6IM3*HL{5=*+IU9GX|&9dY+h~U>KG)9V~MW+!V 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 literal 3943 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 PuZfM7y=9I0ou~f zX>1$E702J~K}pm>9oA(_vSiD0Z6}U{I&I=JX;K75g5E`wpg>SGXwagc0`y8EMLv=O zMT#D1(*_0F0!51+Uy7hj+Am2ROGw~cPTV+gd`q@u(Xy?J6vaKJ*hf)XN~E+T_n2MI z4-1>3$mNRvotZc9y?G-+@Tdl;1P}oT{yPgG0nFi#WdJdNIDm$SfG?=P*M|VE0W<DTxf->NPH)Bz;%v7VOA2|Z$XEI?>Mc0>0>F8%uf&YG8(4#*j0iBA?tvJFZPi798thcZaM)&<@$`cTKoy!7 ziIv~rC9JAmi^mN-{6)$VtU}XL2e|6>^<;xF)n)9=HEcQ)USCWx%-|uQ_5vFJEd4nw zsa}p#?P0Gk!$x!*n^Ja39l$i&p$x!=>Ko7i9%gzhEQ*x>pc-w`8{S*{UaP(iE#7k; zUxiCd1>D7T z)oZYo54ruZETUjrZ^mh!yM9I&RS%&9Yl=&r$ry^ziW7ae8ogVp*P;13;?k%23>E#j zO&ADv#Vh5C>MdxwaLHFDiXMv&H1{0#JC zqGZ0{3J&u%mUBb$$1&Bb(0t>P7se=ldyP&_=$%o$9WB!9j-0zxNTOx89^j(==XOB# zjW`?ee{z}kr~sD$wgG%(v-{R=)jP3Q*KIy)>7oI=kLJ<1o!_dhs<#7Nu=9MJd7_vw zIBwgF&OX&!&<5c+#0bF0FgR(4@HVO5j*|09xublDEq>I76hb>x@8li3TZ{nIqYtOJ z23EA{-6fL~!3fv!J3mv@If_$#9dJBQ!Op1-X_fNc9e%OHPP*Pbb;h0HZPW zUuPiZb)s^?jA8SO=WWvGuX;Pc<-Fg-6!Jq`$yYWLAV7iaXi^?O;`J33!6gjK=II~H zQ}xt9;`3Z)a%YDjIW9&gTN+4h8J(E(`l5;;nz!)zsGhRm91HckN2R>*&=5Z)dU`cQ zqhTjsna6x8AJx}zPdlqjqOoPa>NU7phr=vGn4)OS%C@XJU%J3PQ&wF!)>LoilKX*c zsJaC#s9wv{q5Q*jOCeATsy73ivg@Ka&ZFW~YsE?ctEs*Y7aMbY#3jtLD@t|Me}Pm| zUm-rYg>=`^cca>0KA?&GGY~`~e_=D+-U`uK?XMr`7bh*ehb0o|T*>WdUQzWnUpil< z$8IAxtsy{YSA;~$l%Kzu_ZdFhBYbVE;lk+#vb7=k=zn0&SJ`1xn4f+&=kHfkoiJbJ zEgNK8L(1W5{JXuekTcxYlJeRaM9QJ55{CWO+@rXNcsBblbE{tF+j3BZEIyVB^0rnj zx@=K>AKv@ZSo|mlIaHVNJ$?>Ja18%^B(wf?njEV0q=^6!$7OPPsGegy;P{MbTpPP= z;Y2poYYR!r;jqYcT$8jcIG#gwzN#TGWOKJHQ(f|5nP$!kMA4@{mhs#&)vLMcfn(WR zY?<9=qtXo*h!}9%^q#0 zVx5{Y@%y(ESuh^ow^5o>l)s&H|0=|r8l`V+Pq~AnZ|=~~PSB4SuE&zZy~<2vsvc&& zt&7c-M4ETK5kmCth&R`ORHYT z{G5$bJn2q8tvU~-mjX*w=adK-ctzD^)}1qp#8pZuS(v8lUS0R%=Tb^R^U2p2!=Abcx9_@qq-Z*_={^lcu(DN*9$GI}iMhPoa-6wadK}=DfeM9`# ztq=*(SBA8Ic(W=;`(T{>{)F(=Eg*pQ=ac#=w%xJIK-HW4v`SffYgBv0`*rl{3#Z`) zA1(_*vmI6EGxgG8)X1rl45N-u+e!!B$e5Ol70&@EHN&xv2oiU{mmCU6#>ys6Nuh$o zO}Ubz3?#po40ME~-`4Es1pz5|o_Km;igc#)>8=!1$^6U&bf;5K6rD^YeB{Skodih1 zo3?2`dxJc&3mU_8bVhyd?E=oA{PBd;92Ryo!>pn{_con&c(YuW2?x=J*Dp*aAN@Ck zWfE6tVd0sWq8=E4N|_{esswHbOr*Rt!9%AolPtDdjwyla*cX@qiErUiawvS}R+313 z>%9_$t6qwr4Ielwm9sKj^-@GKlIDWtgy5`-WF$@ct!^TxD1@MTX*erT1yaF#eRBLt zHAu$JO+HD6=h(vykc`bjI359nD z>{p(Am0r!e9}{ZJr9=Cmy&6km5NNFu_O@&PJmbhky9`#~Mb!i2n^SdjjZxBEDIeaa z^u9`?c?d5=%A`a4NPFfENm@|WMJ%c=;}8B%prdbGp{H+n}?>I;}!wMlg)n9V&UymDYL)VGWF z)aMeO4o@5N@}NrI0Wh@qQ)b|W?OD69sh-rAYwt!Ok#_`)#*)eYfzZ?YX;);q@J3aO zzN3ZB%ZVtETbe-8^^;fGEgmgr{FJHs6@VH>-SYBxc9Oe0j5~93PU#z`(IaHsVPpjkj7%8XQEr;qc;1$GuPYb|s_TS`A zik@QEW5Zb;d8Lp_Zq-#B71&grtywVzun^62nVKvUwgpyP0C59*2^MyRnNbL(i;kMQ zidA0k7G#N+hLKXkl^MIOFK0#76=sT(-AK@cMk?m7)<};jmkNl%*beDg%+~-);W8UJ zXyq$4bPJ1}CYA>2Jb?n}r>==Rno~a#Q4nu!)?V{CM_p(PlZDL#47aALUu0lJ46i=m ztfsnx3m+x$zVPU=QAn!}6vWSM(q6yp?Ovn8=X=Z+hJHM1Grz$fks+uZm%|#1nGe-!k0Ijds6z=nEpR+zaTW? zto6#)RFCIzM(5C8JR^L%6WY>=>x2k9n&jv1)n7QRzkE^8f9-0%2sLHolWWB<_CRZu zdFZ4GRr~F6r$2pl8UZd?dZFc|>sP%G$xuaCe{xJZwAUP8CzZ1J;8yX$t&r6883(i8 zNSR0~vw1ivXwM(l$Glz+-U47bpCg}w1v9v3ceJpJvIqVAg!=3;>9J2hjZBu9sv%LR zcXu9dm~K>i{$1^ri>`j|a#9)nmV4rSRFC1Rs4|cDaMq8HsZSh{es~A0t+McP2chw# z_RF_DpFjc@4$RA$mS=I2F@SYTld#-JAD&8n?`8Sn?c`G(AP`H-8*Ic_AVcVTQT6%b zbcBJdqn5m9SoxQvi2}6e4Cc7UjH3MNE#Xh^lRMT5w=@|ki%nr?-Qg4VPfZ5~=x364u5<7VY z@-G5N6L#$OWoOx1B%I=vZ2~yU7f0Gho6V9tP#Rvvd1NQH`ydu_a+@9;I*YT|m*dQa z02gpN7l=C4Ua}S^#tz`vHW1ga*h#g=Sr~q$8oxcq0-H{5{xN};Cj;?#pV5G_I2p@! zc*~b54A8Y=i|6ioE38E)jKfKI&TsUiqGi{Ds(YO}%rxZ~#*DfVzjkYTrz@CSy&iM) z8B0xIf+jS6-6~q<;-+3PJT_tv7PxA)g;R+G`G~8T3a)SEb{7B6x-5l}N|6Yb0CIit zs23afr5gQatZG-Xe)}-SOozQmM)Gdk##3m&w4wHFq?@`#7^mYZI^8_N)6F^)Xurq+ z8!!>i>or4;Bd7KDx|SaUTeK>e^;d!4h!&iEf-{h?KflgOK#~E~Q=~lh5uDvkv-0iO zKn)(0Ee9nts;8L5BZTfe&F4P+Mx26AxHk*84r8jPNT##!n(=YmS3%nleyKIsU9q-G zBn^|QFOaf&BG~j5bm+4BJsPaX$@eVYvTR!?Y^$DPGCj46q_?V=Wu}6C1vYB|53d)o zTgMNoFJPY5H(~2fHGP+-d=k1BQ!}wy@26n};49V5EEsyBbPhrYhyM|0Qudm#Su6Mv zbG~;g`BwD>l6Wk1`1SZVrM9!s(mPts;gdSPENc#$fXz3xKXC*_m2r?J_$vjY2ELw9 aWb%Jqbod*sW)h+R00004AOQgA3Q($ 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 literal 72121 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 Date: Mon, 27 Apr 2026 20:43:12 +0200 Subject: [PATCH 2/4] refactor(settings): split download/options into focused pages - Extract files, metadata, lyrics into dedicated pages - Move search source + fallback into download page - Move app/update/debug settings into new app_settings_page - Replace options_settings_page with app_settings_page - Reorganize settings_tab into 3 logical groups --- lib/screens/settings/app_settings_page.dart | 372 +++ .../settings/download_settings_page.dart | 2046 ++++------------- lib/screens/settings/files_settings_page.dart | 1055 +++++++++ .../settings/lyrics_settings_page.dart | 373 +++ .../settings/metadata_settings_page.dart | 253 ++ .../settings/options_settings_page.dart | 1012 -------- lib/screens/settings/settings_tab.dart | 106 +- 7 files changed, 2547 insertions(+), 2670 deletions(-) create mode 100644 lib/screens/settings/app_settings_page.dart create mode 100644 lib/screens/settings/files_settings_page.dart create mode 100644 lib/screens/settings/lyrics_settings_page.dart create mode 100644 lib/screens/settings/metadata_settings_page.dart delete mode 100644 lib/screens/settings/options_settings_page.dart diff --git a/lib/screens/settings/app_settings_page.dart b/lib/screens/settings/app_settings_page.dart new file mode 100644 index 00000000..8b7d14bf --- /dev/null +++ b/lib/screens/settings/app_settings_page.dart @@ -0,0 +1,372 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:spotiflac_android/l10n/l10n.dart'; +import 'package:spotiflac_android/providers/download_queue_provider.dart'; +import 'package:spotiflac_android/providers/settings_provider.dart'; +import 'package:spotiflac_android/utils/app_bar_layout.dart'; +import 'package:spotiflac_android/widgets/settings_group.dart'; + +class AppSettingsPage extends ConsumerWidget { + const AppSettingsPage({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final settings = ref.watch(settingsProvider); + final colorScheme = Theme.of(context).colorScheme; + final topPadding = normalizedHeaderTopPadding(context); + + return PopScope( + canPop: true, + child: Scaffold( + body: CustomScrollView( + slivers: [ + SliverAppBar( + expandedHeight: 120 + topPadding, + collapsedHeight: kToolbarHeight, + floating: false, + pinned: true, + backgroundColor: colorScheme.surface, + surfaceTintColor: Colors.transparent, + leading: IconButton( + tooltip: MaterialLocalizations.of(context).backButtonTooltip, + icon: const Icon(Icons.arrow_back), + onPressed: () => Navigator.pop(context), + ), + flexibleSpace: LayoutBuilder( + builder: (context, constraints) { + final maxHeight = 120 + topPadding; + final minHeight = kToolbarHeight + topPadding; + final expandRatio = + ((constraints.maxHeight - minHeight) / + (maxHeight - minHeight)) + .clamp(0.0, 1.0); + final leftPadding = 56 - (32 * expandRatio); + return FlexibleSpaceBar( + expandedTitleScale: 1.0, + titlePadding: EdgeInsets.only( + left: leftPadding, + bottom: 16, + ), + title: Text( + context.l10n.settingsApp, + style: TextStyle( + fontSize: 20 + (8 * expandRatio), + fontWeight: FontWeight.bold, + color: colorScheme.onSurface, + ), + ), + ); + }, + ), + ), + + // ── Updates ──────────────────────────────────────────────── + SliverToBoxAdapter( + child: SettingsSectionHeader(title: context.l10n.sectionApp), + ), + SliverToBoxAdapter( + child: SettingsGroup( + children: [ + SettingsSwitchItem( + icon: Icons.extension, + title: context.l10n.optionsExtensionStore, + subtitle: context.l10n.optionsExtensionStoreSubtitle, + value: settings.showExtensionStore, + onChanged: (v) => ref + .read(settingsProvider.notifier) + .setShowExtensionStore(v), + ), + SettingsSwitchItem( + icon: Icons.system_update, + title: context.l10n.optionsCheckUpdates, + subtitle: context.l10n.optionsCheckUpdatesSubtitle, + value: settings.checkForUpdates, + onChanged: (v) => ref + .read(settingsProvider.notifier) + .setCheckForUpdates(v), + showDivider: settings.checkForUpdates, + ), + if (settings.checkForUpdates) + _UpdateChannelSelector( + currentChannel: settings.updateChannel, + onChanged: (v) => ref + .read(settingsProvider.notifier) + .setUpdateChannel(v), + ), + ], + ), + ), + + // ── Data ─────────────────────────────────────────────────── + SliverToBoxAdapter( + child: SettingsSectionHeader(title: context.l10n.sectionData), + ), + SliverToBoxAdapter( + child: SettingsGroup( + children: [ + SettingsItem( + icon: Icons.cleaning_services_outlined, + title: context.l10n.cleanupOrphanedDownloads, + subtitle: context.l10n.cleanupOrphanedDownloadsSubtitle, + onTap: () => _cleanupOrphanedDownloads(context, ref), + ), + SettingsItem( + icon: Icons.delete_forever, + title: context.l10n.optionsClearHistory, + subtitle: context.l10n.optionsClearHistorySubtitle, + onTap: () => + _showClearHistoryDialog(context, ref, colorScheme), + showDivider: false, + ), + ], + ), + ), + + // ── Debug ────────────────────────────────────────────────── + SliverToBoxAdapter( + child: SettingsSectionHeader(title: context.l10n.sectionDebug), + ), + SliverToBoxAdapter( + child: SettingsGroup( + children: [ + SettingsSwitchItem( + icon: Icons.bug_report, + title: context.l10n.optionsDetailedLogging, + subtitle: settings.enableLogging + ? context.l10n.optionsDetailedLoggingOn + : context.l10n.optionsDetailedLoggingOff, + value: settings.enableLogging, + onChanged: (v) => ref + .read(settingsProvider.notifier) + .setEnableLogging(v), + showDivider: false, + ), + ], + ), + ), + + const SliverToBoxAdapter(child: SizedBox(height: 32)), + ], + ), + ), + ); + } + + void _showClearHistoryDialog( + BuildContext context, + WidgetRef ref, + ColorScheme colorScheme, + ) { + showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text(context.l10n.dialogClearHistoryTitle), + content: Text(context.l10n.dialogClearHistoryMessage), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context), + child: Text(context.l10n.dialogCancel), + ), + TextButton( + onPressed: () { + ref.read(downloadHistoryProvider.notifier).clearHistory(); + Navigator.pop(context); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(context.l10n.snackbarHistoryCleared)), + ); + }, + child: Text( + context.l10n.dialogClear, + style: TextStyle(color: colorScheme.error), + ), + ), + ], + ), + ); + } + + Future _cleanupOrphanedDownloads( + BuildContext context, + WidgetRef ref, + ) async { + showDialog( + context: context, + barrierDismissible: false, + builder: (context) => AlertDialog( + content: Row( + children: [ + const CircularProgressIndicator(), + const SizedBox(width: 16), + Text(context.l10n.cleanupOrphanedDownloads), + ], + ), + ), + ); + try { + final removed = await ref + .read(downloadHistoryProvider.notifier) + .cleanupOrphanedDownloads(); + if (context.mounted) { + Navigator.pop(context); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + removed > 0 + ? context.l10n.cleanupOrphanedDownloadsResult(removed) + : context.l10n.cleanupOrphanedDownloadsNone, + ), + ), + ); + } + } catch (e) { + if (context.mounted) { + Navigator.pop(context); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(context.l10n.snackbarError(e.toString()))), + ); + } + } + } +} + +class _UpdateChannelSelector extends StatelessWidget { + final String currentChannel; + final ValueChanged onChanged; + const _UpdateChannelSelector({ + required this.currentChannel, + required this.onChanged, + }); + + @override + Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + final isDark = Theme.of(context).brightness == Brightness.dark; + final unselectedColor = isDark + ? Color.alphaBlend( + Colors.white.withValues(alpha: 0.05), + colorScheme.surface, + ) + : colorScheme.surfaceContainerHigh; + + return Padding( + padding: const EdgeInsets.all(20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Icon( + Icons.new_releases, + color: colorScheme.onSurfaceVariant, + size: 24, + ), + const SizedBox(width: 16), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + context.l10n.optionsUpdateChannel, + style: Theme.of(context).textTheme.bodyLarge, + ), + const SizedBox(height: 2), + Text( + currentChannel == 'preview' + ? context.l10n.optionsUpdateChannelPreview + : context.l10n.optionsUpdateChannelStable, + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + color: colorScheme.onSurfaceVariant, + ), + ), + ], + ), + ), + ], + ), + const SizedBox(height: 16), + Row( + children: [ + _ChannelChip( + label: context.l10n.channelStable, + isSelected: currentChannel == 'stable', + onTap: () => onChanged('stable'), + ), + const SizedBox(width: 8), + _ChannelChip( + label: context.l10n.channelPreview, + isSelected: currentChannel == 'preview', + onTap: () => onChanged('preview'), + ), + ], + ), + const SizedBox(height: 12), + Row( + children: [ + Icon( + Icons.info_outline, + size: 16, + color: colorScheme.onSurfaceVariant, + ), + const SizedBox(width: 8), + Expanded( + child: Text( + context.l10n.optionsUpdateChannelWarning, + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: colorScheme.onSurfaceVariant, + ), + ), + ), + ], + ), + ], + ), + ); + } +} + +class _ChannelChip extends StatelessWidget { + final String label; + final bool isSelected; + final VoidCallback onTap; + const _ChannelChip({ + required this.label, + required this.isSelected, + required this.onTap, + }); + + @override + Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + final isDark = Theme.of(context).brightness == Brightness.dark; + final unselectedColor = isDark + ? Color.alphaBlend( + Colors.white.withValues(alpha: 0.05), + colorScheme.surface, + ) + : colorScheme.surfaceContainerHigh; + return Expanded( + child: Material( + color: isSelected ? colorScheme.primaryContainer : unselectedColor, + borderRadius: BorderRadius.circular(12), + child: InkWell( + onTap: onTap, + borderRadius: BorderRadius.circular(12), + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 12), + child: Center( + child: Text( + label, + style: TextStyle( + fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal, + color: isSelected + ? colorScheme.onPrimaryContainer + : colorScheme.onSurfaceVariant, + ), + ), + ), + ), + ), + ), + ); + } +} diff --git a/lib/screens/settings/download_settings_page.dart b/lib/screens/settings/download_settings_page.dart index 543dbc1f..2a2de59f 100644 --- a/lib/screens/settings/download_settings_page.dart +++ b/lib/screens/settings/download_settings_page.dart @@ -1,18 +1,13 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:file_picker/file_picker.dart'; -import 'package:path_provider/path_provider.dart'; -import 'package:permission_handler/permission_handler.dart'; import 'package:device_info_plus/device_info_plus.dart'; import 'package:spotiflac_android/l10n/l10n.dart'; import 'package:spotiflac_android/models/settings.dart'; import 'package:spotiflac_android/providers/settings_provider.dart'; import 'package:spotiflac_android/providers/extension_provider.dart'; -import 'package:spotiflac_android/services/platform_bridge.dart'; import 'package:spotiflac_android/utils/app_bar_layout.dart'; -import 'package:spotiflac_android/utils/file_access.dart'; -import 'package:spotiflac_android/screens/settings/lyrics_provider_priority_page.dart'; +import 'package:spotiflac_android/screens/settings/download_fallback_extensions_page.dart'; import 'package:spotiflac_android/widgets/settings_group.dart'; class DownloadSettingsPage extends ConsumerStatefulWidget { @@ -25,278 +20,12 @@ class DownloadSettingsPage extends ConsumerStatefulWidget { class _DownloadSettingsPageState extends ConsumerState { static const _builtInServices = ['tidal', 'qobuz']; - static const _songLinkRegions = [ - 'AD', - 'AE', - 'AG', - 'AL', - 'AM', - 'AO', - 'AR', - 'AT', - 'AU', - 'AZ', - 'BA', - 'BB', - 'BD', - 'BE', - 'BF', - 'BG', - 'BH', - 'BI', - 'BJ', - 'BN', - 'BO', - 'BR', - 'BS', - 'BT', - 'BW', - 'BZ', - 'CA', - 'CD', - 'CG', - 'CH', - 'CI', - 'CL', - 'CM', - 'CO', - 'CR', - 'CV', - 'CW', - 'CY', - 'CZ', - 'DE', - 'DJ', - 'DK', - 'DM', - 'DO', - 'DZ', - 'EC', - 'EE', - 'EG', - 'ES', - 'ET', - 'FI', - 'FJ', - 'FM', - 'FR', - 'GA', - 'GB', - 'GD', - 'GE', - 'GH', - 'GM', - 'GN', - 'GQ', - 'GR', - 'GT', - 'GW', - 'GY', - 'HK', - 'HN', - 'HR', - 'HT', - 'HU', - 'ID', - 'IE', - 'IL', - 'IN', - 'IQ', - 'IS', - 'IT', - 'JM', - 'JO', - 'JP', - 'KE', - 'KG', - 'KH', - 'KI', - 'KM', - 'KN', - 'KR', - 'KW', - 'KZ', - 'LA', - 'LB', - 'LC', - 'LI', - 'LK', - 'LR', - 'LS', - 'LT', - 'LU', - 'LV', - 'LY', - 'MA', - 'MC', - 'MD', - 'ME', - 'MG', - 'MH', - 'MK', - 'ML', - 'MN', - 'MO', - 'MR', - 'MT', - 'MU', - 'MV', - 'MW', - 'MX', - 'MY', - 'MZ', - 'NA', - 'NE', - 'NG', - 'NI', - 'NL', - 'NO', - 'NP', - 'NR', - 'NZ', - 'OM', - 'PA', - 'PE', - 'PG', - 'PH', - 'PK', - 'PL', - 'PS', - 'PT', - 'PW', - 'PY', - 'QA', - 'RO', - 'RS', - 'RW', - 'SA', - 'SB', - 'SC', - 'SE', - 'SG', - 'SI', - 'SK', - 'SL', - 'SM', - 'SN', - 'SR', - 'ST', - 'SV', - 'SZ', - 'TD', - 'TG', - 'TH', - 'TJ', - 'TL', - 'TN', - 'TO', - 'TR', - 'TT', - 'TV', - 'TW', - 'TZ', - 'UA', - 'UG', - 'US', - 'UY', - 'UZ', - 'VC', - 'VE', - 'VN', - 'VU', - 'WS', - 'XK', - 'ZA', - 'ZM', - 'ZW', - ]; - static const _songLinkRegionNames = { - 'US': 'United States', - 'GB': 'United Kingdom', - 'FR': 'France', - 'DE': 'Germany', - 'JP': 'Japan', - 'KR': 'South Korea', - 'IN': 'India', - 'ID': 'Indonesia', - 'BR': 'Brazil', - 'MX': 'Mexico', - 'AU': 'Australia', - 'CA': 'Canada', - 'XK': 'Kosovo', - }; - int _androidSdkVersion = 0; - bool _hasAllFilesAccess = false; - bool _artistFolderFiltersExpanded = false; - - @override - void initState() { - super.initState(); - _initDeviceInfo(); - } - - Future _initDeviceInfo() async { - if (Platform.isAndroid) { - final deviceInfo = DeviceInfoPlugin(); - final androidInfo = await deviceInfo.androidInfo; - final sdkVersion = androidInfo.version.sdkInt; - final hasAccess = await Permission.manageExternalStorage.isGranted; - if (mounted) { - setState(() { - _androidSdkVersion = sdkVersion; - _hasAllFilesAccess = hasAccess; - }); - } - } - } - - Future _requestAllFilesAccess() async { - final status = await Permission.manageExternalStorage.request(); - if (status.isGranted) { - ref.read(settingsProvider.notifier).setUseAllFilesAccess(true); - if (mounted) { - setState(() => _hasAllFilesAccess = true); - } - } else if (status.isPermanentlyDenied) { - if (mounted) { - final shouldOpen = await showDialog( - context: context, - builder: (context) => AlertDialog( - title: Text(context.l10n.setupStorageAccessRequired), - content: Text(context.l10n.allFilesAccessDeniedMessage), - actions: [ - TextButton( - onPressed: () => Navigator.pop(context, false), - child: Text(context.l10n.dialogCancel), - ), - FilledButton( - onPressed: () => Navigator.pop(context, true), - child: Text(context.l10n.setupOpenSettings), - ), - ], - ), - ); - if (shouldOpen == true) { - await openAppSettings(); - } - } - } - } - - Future _disableAllFilesAccess() async { - ref.read(settingsProvider.notifier).setUseAllFilesAccess(false); - // Note: We can't revoke the permission programmatically, - // but we can stop using it in the app - if (mounted) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(context.l10n.allFilesAccessDisabledMessage)), - ); - } - } @override Widget build(BuildContext context) { final settings = ref.watch(settingsProvider); + final extensionState = ref.watch(extensionProvider); + final hasExtensions = extensionState.extensions.isNotEmpty; final colorScheme = Theme.of(context).colorScheme; final topPadding = normalizedHeaderTopPadding(context); @@ -336,7 +65,7 @@ class _DownloadSettingsPageState extends ConsumerState { bottom: 16, ), title: Text( - context.l10n.downloadTitle, + context.l10n.settingsDownload, style: TextStyle( fontSize: 20 + (8 * expandRatio), fontWeight: FontWeight.bold, @@ -348,6 +77,7 @@ class _DownloadSettingsPageState extends ConsumerState { ), ), + // ── Service ──────────────────────────────────────────────── SliverToBoxAdapter( child: SettingsSectionHeader(title: context.l10n.sectionService), ), @@ -364,6 +94,7 @@ class _DownloadSettingsPageState extends ConsumerState { ), ), + // ── Audio Quality ────────────────────────────────────────── SliverToBoxAdapter( child: SettingsSectionHeader( title: context.l10n.sectionAudioQuality, @@ -411,7 +142,6 @@ class _DownloadSettingsPageState extends ConsumerState { .setAudioQuality('HI_RES_LOSSLESS'), showDivider: isTidalService, ), - // Lossy 320kbps option (Tidal only) - downloads M4A AAC from server, converts to MP3/Opus if (isTidalService) _QualityOption( title: context.l10n.downloadLossy320, @@ -441,7 +171,7 @@ class _DownloadSettingsPageState extends ConsumerState { showDivider: false, ), ], - if (!isBuiltInService) ...[ + if (!isBuiltInService) Padding( padding: const EdgeInsets.fromLTRB(16, 8, 16, 16), child: Row( @@ -464,278 +194,25 @@ class _DownloadSettingsPageState extends ConsumerState { ], ), ), - ], - ], - ), - ), - - SliverToBoxAdapter( - child: SettingsSectionHeader(title: context.l10n.sectionLyrics), - ), - SliverToBoxAdapter( - child: SettingsGroup( - children: [ - SettingsSwitchItem( - icon: Icons.subtitles_outlined, - title: context.l10n.optionsEmbedLyrics, - subtitle: settings.embedMetadata - ? context.l10n.optionsEmbedLyricsSubtitle - : context.l10n.downloadEmbedLyricsDisabled, - value: settings.embedLyrics, - enabled: settings.embedMetadata, - onChanged: (value) => ref - .read(settingsProvider.notifier) - .setEmbedLyrics(value), - showDivider: settings.embedMetadata && settings.embedLyrics, - ), - if (settings.embedMetadata && settings.embedLyrics) ...[ - SettingsItem( - icon: Icons.lyrics_outlined, - title: context.l10n.lyricsMode, - subtitle: _getLyricsModeLabel( - context, - settings.lyricsMode, - ), - onTap: () => _showLyricsModePicker( - context, - ref, - settings.lyricsMode, - ), - ), - SettingsItem( - icon: Icons.source_outlined, - title: context.l10n.lyricsProvidersTitle, - subtitle: _getLyricsProvidersSubtitle( - settings.lyricsProviders, - ), - onTap: () => Navigator.push( - context, - MaterialPageRoute( - builder: (_) => const LyricsProviderPriorityPage(), - ), - ), - ), - SettingsSwitchItem( - icon: Icons.translate_outlined, - title: context.l10n.downloadNeteaseIncludeTranslation, - subtitle: settings.lyricsIncludeTranslationNetease - ? context - .l10n - .downloadNeteaseIncludeTranslationEnabled - : context - .l10n - .downloadNeteaseIncludeTranslationDisabled, - value: settings.lyricsIncludeTranslationNetease, - onChanged: (value) => ref - .read(settingsProvider.notifier) - .setLyricsIncludeTranslationNetease(value), - ), - SettingsSwitchItem( - icon: Icons.text_fields_outlined, - title: context.l10n.downloadNeteaseIncludeRomanization, - subtitle: settings.lyricsIncludeRomanizationNetease - ? context - .l10n - .downloadNeteaseIncludeRomanizationEnabled - : context - .l10n - .downloadNeteaseIncludeRomanizationDisabled, - value: settings.lyricsIncludeRomanizationNetease, - onChanged: (value) => ref - .read(settingsProvider.notifier) - .setLyricsIncludeRomanizationNetease(value), - ), - SettingsSwitchItem( - icon: Icons.record_voice_over_outlined, - title: context.l10n.downloadAppleQqMultiPerson, - subtitle: settings.lyricsMultiPersonWordByWord - ? context.l10n.downloadAppleQqMultiPersonEnabled - : context.l10n.downloadAppleQqMultiPersonDisabled, - value: settings.lyricsMultiPersonWordByWord, - onChanged: (value) => ref - .read(settingsProvider.notifier) - .setLyricsMultiPersonWordByWord(value), - ), - SettingsItem( - icon: Icons.language_outlined, - title: context.l10n.downloadMusixmatchLanguage, - subtitle: settings.musixmatchLanguage.isEmpty - ? context.l10n.downloadMusixmatchLanguageAuto - : settings.musixmatchLanguage.toUpperCase(), - onTap: () => _showMusixmatchLanguagePicker( - context, - ref, - settings.musixmatchLanguage, - ), - showDivider: false, - ), - ], ], ), ), + // ── Network & Performance ────────────────────────────────── SliverToBoxAdapter( child: SettingsSectionHeader( - title: context.l10n.sectionFileSettings, + title: context.l10n.sectionPerformance, ), ), SliverToBoxAdapter( child: SettingsGroup( children: [ - SettingsItem( - icon: Icons.text_fields, - title: context.l10n.downloadFilenameFormat, - subtitle: settings.filenameFormat, - onTap: () => _showFormatEditor( - context, - ref, - settings.filenameFormat, - ), - ), - SettingsItem( - icon: Icons.music_note_outlined, - title: context.l10n.downloadSingleFilenameFormat, - subtitle: settings.singleFilenameFormat, - onTap: () => _showFormatEditor( - context, - ref, - settings.singleFilenameFormat, - onSave: ref - .read(settingsProvider.notifier) - .setSingleFilenameFormat, - title: context.l10n.downloadSingleFilenameFormat, - description: - context.l10n.downloadSingleFilenameFormatDescription, - ), - ), - SettingsItem( - icon: Icons.folder_outlined, - title: context.l10n.downloadDirectory, - subtitle: settings.downloadDirectory.isEmpty - ? (Platform.isIOS - ? context.l10n.setupAppDocumentsFolder - : 'Music/SpotiFLAC') - : settings.downloadDirectory, - onTap: () => _pickDirectory(context, ref), - ), - SettingsSwitchItem( - icon: Icons.library_music_outlined, - title: context.l10n.downloadSeparateSinglesFolder, - subtitle: settings.separateSingles - ? context.l10n.downloadSeparateSinglesEnabled - : context.l10n.downloadSeparateSinglesDisabled, - value: settings.separateSingles, - onChanged: (value) => ref + _ConcurrentDownloadsItem( + currentValue: settings.concurrentDownloads, + onChanged: (v) => ref .read(settingsProvider.notifier) - .setSeparateSingles(value), + .setConcurrentDownloads(v), ), - if (settings.separateSingles) - SettingsItem( - icon: Icons.folder_outlined, - title: context.l10n.downloadAlbumFolderStructure, - subtitle: _getAlbumFolderStructureLabel( - settings.albumFolderStructure, - ), - onTap: () => _showAlbumFolderStructurePicker( - context, - ref, - settings.albumFolderStructure, - ), - ), - if (!settings.separateSingles) - SettingsItem( - icon: Icons.create_new_folder_outlined, - title: context.l10n.downloadFolderOrganization, - subtitle: _getFolderOrganizationLabel( - settings.folderOrganization, - ), - onTap: () => _showFolderOrganizationPicker( - context, - ref, - settings.folderOrganization, - ), - ), - SettingsSwitchItem( - icon: Icons.playlist_play_outlined, - title: context.l10n.downloadCreatePlaylistSourceFolder, - subtitle: _getPlaylistFolderSubtitle(settings), - value: settings.createPlaylistFolder, - onChanged: (value) => ref - .read(settingsProvider.notifier) - .setCreatePlaylistFolder(value), - ), - SettingsSwitchItem( - icon: Icons.person_search_outlined, - title: context.l10n.downloadUseAlbumArtistForFolders, - subtitle: settings.useAlbumArtistForFolders - ? context - .l10n - .downloadUseAlbumArtistForFoldersAlbumSubtitle - : context - .l10n - .downloadUseAlbumArtistForFoldersTrackSubtitle, - value: settings.useAlbumArtistForFolders, - onChanged: (value) => ref - .read(settingsProvider.notifier) - .setUseAlbumArtistForFolders(value), - ), - SettingsItem( - icon: Icons.filter_alt_outlined, - title: context.l10n.downloadArtistNameFilters, - subtitle: _getArtistFolderFilterSubtitle( - context, - usePrimaryArtistOnly: settings.usePrimaryArtistOnly, - filterAlbumArtistContributors: - settings.filterContributingArtistsInAlbumArtist, - ), - trailing: Icon( - _artistFolderFiltersExpanded - ? Icons.expand_less - : Icons.expand_more, - ), - onTap: () { - setState(() { - _artistFolderFiltersExpanded = - !_artistFolderFiltersExpanded; - }); - }, - showDivider: !_artistFolderFiltersExpanded, - ), - if (_artistFolderFiltersExpanded) - SettingsSwitchItem( - icon: Icons.person_outline, - title: context.l10n.downloadUsePrimaryArtistOnly, - subtitle: settings.usePrimaryArtistOnly - ? context.l10n.downloadUsePrimaryArtistOnlyEnabled - : context.l10n.downloadUsePrimaryArtistOnlyDisabled, - value: settings.usePrimaryArtistOnly, - onChanged: (value) => ref - .read(settingsProvider.notifier) - .setUsePrimaryArtistOnly(value), - ), - if (_artistFolderFiltersExpanded) - SettingsSwitchItem( - icon: Icons.group_remove_outlined, - title: context.l10n.downloadFilterContributing, - subtitle: settings.filterContributingArtistsInAlbumArtist - ? context.l10n.downloadFilterContributingEnabled - : context.l10n.downloadFilterContributingDisabled, - value: settings.filterContributingArtistsInAlbumArtist, - onChanged: (value) => ref - .read(settingsProvider.notifier) - .setFilterContributingArtistsInAlbumArtist(value), - showDivider: false, - ), - ], - ), - ), - - SliverToBoxAdapter( - child: SettingsSectionHeader(title: context.l10n.sectionDownload), - ), - SliverToBoxAdapter( - child: SettingsGroup( - children: [ SettingsItem( icon: Icons.wifi, title: context.l10n.settingsDownloadNetwork, @@ -748,6 +225,77 @@ class _DownloadSettingsPageState extends ConsumerState { settings.downloadNetworkMode, ), ), + SettingsSwitchItem( + icon: Icons.security_outlined, + title: context.l10n.downloadNetworkCompatibilityMode, + subtitle: settings.networkCompatibilityMode + ? context.l10n.downloadNetworkCompatibilityModeEnabled + : context.l10n.downloadNetworkCompatibilityModeDisabled, + value: settings.networkCompatibilityMode, + onChanged: (value) => ref + .read(settingsProvider.notifier) + .setNetworkCompatibilityMode(value), + showDivider: false, + ), + ], + ), + ), + + // ── Fallback & Search ────────────────────────────────────── + SliverToBoxAdapter( + child: SettingsSectionHeader( + title: context.l10n.sectionSearchSource, + ), + ), + SliverToBoxAdapter( + child: SettingsGroup( + children: [ + const _MetadataSourceSelector(), + const _DefaultSearchTabSelector(), + SettingsSwitchItem( + icon: Icons.sync, + title: context.l10n.optionsAutoFallback, + subtitle: context.l10n.optionsAutoFallbackSubtitle, + value: settings.autoFallback, + onChanged: (v) => + ref.read(settingsProvider.notifier).setAutoFallback(v), + ), + if (hasExtensions) + SettingsSwitchItem( + icon: Icons.extension, + title: context.l10n.optionsUseExtensionProviders, + subtitle: settings.useExtensionProviders + ? context.l10n.optionsUseExtensionProvidersOn + : context.l10n.optionsUseExtensionProvidersOff, + value: settings.useExtensionProviders, + onChanged: (v) => ref + .read(settingsProvider.notifier) + .setUseExtensionProviders(v), + ), + SettingsItem( + icon: Icons.extension_outlined, + title: context.l10n.downloadFallbackExtensions, + subtitle: context.l10n.downloadFallbackExtensionsSubtitle, + onTap: () => Navigator.push( + context, + MaterialPageRoute( + builder: (_) => + const DownloadFallbackExtensionsPage(), + ), + ), + showDivider: false, + ), + ], + ), + ), + + // ── Misc ─────────────────────────────────────────────────── + SliverToBoxAdapter( + child: SettingsSectionHeader(title: context.l10n.sectionDownload), + ), + SliverToBoxAdapter( + child: SettingsGroup( + children: [ SettingsItem( icon: Icons.public, title: context.l10n.downloadSongLinkRegion, @@ -758,88 +306,20 @@ class _DownloadSettingsPageState extends ConsumerState { settings.songLinkRegion, ), ), - SettingsSwitchItem( - icon: Icons.security_outlined, - title: context.l10n.downloadNetworkCompatibilityMode, - subtitle: settings.networkCompatibilityMode - ? context.l10n.downloadNetworkCompatibilityModeEnabled - : context.l10n.downloadNetworkCompatibilityModeDisabled, - value: settings.networkCompatibilityMode, - onChanged: (value) { - ref - .read(settingsProvider.notifier) - .setNetworkCompatibilityMode(value); - }, - ), SettingsSwitchItem( icon: Icons.file_download_outlined, title: context.l10n.settingsAutoExportFailed, subtitle: context.l10n.settingsAutoExportFailedSubtitle, value: settings.autoExportFailedDownloads, - onChanged: (value) { - ref - .read(settingsProvider.notifier) - .setAutoExportFailedDownloads(value); - }, + onChanged: (value) => ref + .read(settingsProvider.notifier) + .setAutoExportFailedDownloads(value), showDivider: false, ), ], ), ), - if (Platform.isAndroid && _androidSdkVersion >= 33) ...[ - SliverToBoxAdapter( - child: SettingsSectionHeader( - title: context.l10n.sectionStorageAccess, - ), - ), - SliverToBoxAdapter( - child: SettingsGroup( - children: [ - SettingsSwitchItem( - icon: Icons.folder_special_outlined, - title: context.l10n.allFilesAccess, - subtitle: _hasAllFilesAccess - ? context.l10n.allFilesAccessEnabledSubtitle - : context.l10n.allFilesAccessDisabledSubtitle, - value: _hasAllFilesAccess && settings.useAllFilesAccess, - onChanged: (value) { - if (value) { - _requestAllFilesAccess(); - } else { - _disableAllFilesAccess(); - } - }, - showDivider: false, - ), - ], - ), - ), - SliverToBoxAdapter( - child: Padding( - padding: const EdgeInsets.fromLTRB(16, 8, 16, 0), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Icon( - Icons.info_outline, - size: 16, - color: colorScheme.onSurfaceVariant, - ), - const SizedBox(width: 8), - Expanded( - child: Text( - context.l10n.allFilesAccessDescription, - style: Theme.of(context).textTheme.bodySmall - ?.copyWith(color: colorScheme.onSurfaceVariant), - ), - ), - ], - ), - ), - ), - ], - const SliverToBoxAdapter(child: SizedBox(height: 32)), ], ), @@ -847,765 +327,6 @@ class _DownloadSettingsPageState extends ConsumerState { ); } - String _getAlbumFolderStructureLabel(String structure) { - switch (structure) { - case 'album_only': - return 'Albums/Album Name/'; - case 'artist_year_album': - return 'Albums/Artist/[Year] Album/'; - case 'year_album': - return 'Albums/[Year] Album/'; - case 'artist_album_singles': - return 'Artist/Album/ + Artist/Singles/'; - case 'artist_album_flat': - return 'Artist/Album/ + Artist/song.flac'; - default: - return 'Albums/Artist/Album Name/'; - } - } - - void _showAlbumFolderStructurePicker( - BuildContext context, - WidgetRef ref, - String current, - ) { - showModalBottomSheet( - context: context, - useRootNavigator: true, - builder: (context) => SafeArea( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - ListTile( - leading: const Icon(Icons.folder_outlined), - title: Text(context.l10n.albumFolderArtistAlbum), - subtitle: Text(context.l10n.albumFolderArtistAlbumSubtitle), - trailing: current == 'artist_album' - ? const Icon(Icons.check) - : null, - onTap: () { - ref - .read(settingsProvider.notifier) - .setAlbumFolderStructure('artist_album'); - Navigator.pop(context); - }, - ), - ListTile( - leading: const Icon(Icons.calendar_today_outlined), - title: Text(context.l10n.albumFolderArtistYearAlbum), - subtitle: Text(context.l10n.albumFolderArtistYearAlbumSubtitle), - trailing: current == 'artist_year_album' - ? const Icon(Icons.check) - : null, - onTap: () { - ref - .read(settingsProvider.notifier) - .setAlbumFolderStructure('artist_year_album'); - Navigator.pop(context); - }, - ), - ListTile( - leading: const Icon(Icons.album_outlined), - title: Text(context.l10n.albumFolderAlbumOnly), - subtitle: Text(context.l10n.albumFolderAlbumOnlySubtitle), - trailing: current == 'album_only' - ? const Icon(Icons.check) - : null, - onTap: () { - ref - .read(settingsProvider.notifier) - .setAlbumFolderStructure('album_only'); - Navigator.pop(context); - }, - ), - ListTile( - leading: const Icon(Icons.event_outlined), - title: Text(context.l10n.albumFolderYearAlbum), - subtitle: Text(context.l10n.albumFolderYearAlbumSubtitle), - trailing: current == 'year_album' - ? const Icon(Icons.check) - : null, - onTap: () { - ref - .read(settingsProvider.notifier) - .setAlbumFolderStructure('year_album'); - Navigator.pop(context); - }, - ), - ListTile( - leading: const Icon(Icons.person_outlined), - title: Text(context.l10n.albumFolderArtistAlbumSingles), - subtitle: Text( - context.l10n.albumFolderArtistAlbumSinglesSubtitle, - ), - trailing: current == 'artist_album_singles' - ? const Icon(Icons.check) - : null, - onTap: () { - ref - .read(settingsProvider.notifier) - .setAlbumFolderStructure('artist_album_singles'); - Navigator.pop(context); - }, - ), - ListTile( - leading: const Icon(Icons.person_outline_outlined), - title: Text(context.l10n.albumFolderArtistAlbumFlat), - subtitle: Text(context.l10n.albumFolderArtistAlbumFlatSubtitle), - trailing: current == 'artist_album_flat' - ? const Icon(Icons.check) - : null, - onTap: () { - ref - .read(settingsProvider.notifier) - .setAlbumFolderStructure('artist_album_flat'); - Navigator.pop(context); - }, - ), - ], - ), - ), - ); - } - - void _showFormatEditor( - BuildContext context, - WidgetRef ref, - String current, { - void Function(String)? onSave, - String? title, - String? description, - }) { - final controller = TextEditingController(text: current); - final colorScheme = Theme.of(context).colorScheme; - - final basicTags = [ - '{artist}', - '{title}', - '{album}', - '{track}', - '{year}', - '{date}', - '{disc}', - ]; - final advancedTags = [ - '{track_raw}', - '{track:02}', - '{track:1}', - '{date:%Y}', - '{date:%Y-%m-%d}', - '{disc_raw}', - '{disc:02}', - ]; - var showAdvancedTags = RegExp( - r'\{(?:track_raw|disc_raw|track:\d+|disc:\d+|date:[^}]+)\}', - caseSensitive: false, - ).hasMatch(current); - - void insertTag(String tag) { - final text = controller.text; - final selection = controller.selection; - final start = selection.start >= 0 ? selection.start : text.length; - final end = selection.end >= 0 ? selection.end : text.length; - - String insertion = tag; - if (start > 0) { - final before = text.substring(0, start); - if (!before.trim().endsWith('-')) { - insertion = ' - $tag'; - } else if (before.trim().endsWith('-') && !before.endsWith(' ')) { - insertion = ' $tag'; - } - } - - final newText = text.replaceRange(start, end, insertion); - controller.value = TextEditingValue( - text: newText, - selection: TextSelection.collapsed(offset: start + insertion.length), - ); - } - - showModalBottomSheet( - context: context, - useRootNavigator: true, - isScrollControlled: true, - backgroundColor: colorScheme.surface, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical(top: Radius.circular(28)), - ), - builder: (context) => StatefulBuilder( - builder: (context, setModalState) => Padding( - padding: EdgeInsets.only( - bottom: MediaQuery.of(context).viewInsets.bottom, - ), - child: SingleChildScrollView( - child: SafeArea( - child: Padding( - padding: const EdgeInsets.all(24), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Center( - child: Container( - width: 32, - height: 4, - margin: const EdgeInsets.only(bottom: 24), - decoration: BoxDecoration( - color: colorScheme.outlineVariant, - borderRadius: BorderRadius.circular(2), - ), - ), - ), - Text( - title ?? context.l10n.filenameFormat, - style: Theme.of(context).textTheme.headlineSmall - ?.copyWith(fontWeight: FontWeight.bold), - textAlign: TextAlign.center, - ), - const SizedBox(height: 8), - Text( - description ?? context.l10n.downloadFilenameDescription, - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: colorScheme.onSurfaceVariant, - ), - textAlign: TextAlign.center, - ), - const SizedBox(height: 24), - - TextField( - controller: controller, - decoration: InputDecoration( - hintText: '{artist} - {title}', - filled: true, - fillColor: colorScheme.surfaceContainerHighest - .withValues(alpha: 0.3), - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(16), - borderSide: BorderSide.none, - ), - ), - autofocus: true, - ), - const SizedBox(height: 24), - - Text( - context.l10n.downloadFilenameInsertTag, - style: Theme.of(context).textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.bold, - ), - ), - const SizedBox(height: 12), - Wrap( - spacing: 8, - runSpacing: 8, - children: basicTags.map((tag) { - return ActionChip( - label: Text(tag), - onPressed: () => insertTag(tag), - backgroundColor: colorScheme.surfaceContainerHighest - .withValues(alpha: 0.5), - side: BorderSide.none, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - labelStyle: TextStyle( - color: colorScheme.onSurface, - fontWeight: FontWeight.w500, - ), - ); - }).toList(), - ), - const SizedBox(height: 12), - SwitchListTile( - value: showAdvancedTags, - onChanged: (value) => - setModalState(() => showAdvancedTags = value), - contentPadding: EdgeInsets.zero, - title: Text(context.l10n.filenameShowAdvancedTags), - subtitle: Text( - context.l10n.filenameShowAdvancedTagsDescription, - ), - ), - if (showAdvancedTags) ...[ - const SizedBox(height: 8), - Wrap( - spacing: 8, - runSpacing: 8, - children: advancedTags.map((tag) { - return ActionChip( - label: Text(tag), - onPressed: () => insertTag(tag), - backgroundColor: colorScheme.surfaceContainerHighest - .withValues(alpha: 0.5), - side: BorderSide.none, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - labelStyle: TextStyle( - color: colorScheme.onSurface, - fontWeight: FontWeight.w500, - ), - ); - }).toList(), - ), - ], - - const SizedBox(height: 32), - - Row( - children: [ - Expanded( - child: TextButton( - onPressed: () => Navigator.pop(context), - style: TextButton.styleFrom( - padding: const EdgeInsets.symmetric(vertical: 16), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), - ), - ), - child: Text(context.l10n.dialogCancel), - ), - ), - const SizedBox(width: 12), - Expanded( - flex: 2, - child: FilledButton( - onPressed: () { - final save = - onSave ?? - ref - .read(settingsProvider.notifier) - .setFilenameFormat; - save(controller.text); - Navigator.pop(context); - }, - style: FilledButton.styleFrom( - padding: const EdgeInsets.symmetric(vertical: 16), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), - ), - ), - child: Text(context.l10n.dialogSave), - ), - ), - ], - ), - const SizedBox(height: 8), - ], - ), - ), - ), - ), - ), - ), - ); - } - - Future _pickDirectory(BuildContext context, WidgetRef ref) async { - if (Platform.isIOS) { - _showIOSDirectoryOptions(context, ref); - } else if (Platform.isAndroid) { - _showAndroidDirectoryOptions(context, ref); - } - } - - Future _getDefaultAndroidDirectory() async { - final directMusicPath = '/storage/emulated/0/Music/SpotiFLAC'; - try { - final musicDir = Directory(directMusicPath); - if (!await musicDir.exists()) { - await musicDir.create(recursive: true); - } - return musicDir.path; - } catch (_) {} - - try { - final externalDir = await getExternalStorageDirectory(); - if (externalDir != null) { - final musicDir = Directory( - '${externalDir.parent.parent.parent.parent.path}/Music/SpotiFLAC', - ); - if (!await musicDir.exists()) { - await musicDir.create(recursive: true); - } - return musicDir.path; - } - } catch (_) {} - - final appDir = await getApplicationDocumentsDirectory(); - final fallbackDir = Directory('${appDir.path}/SpotiFLAC'); - if (!await fallbackDir.exists()) { - await fallbackDir.create(recursive: true); - } - return fallbackDir.path; - } - - void _showAndroidDirectoryOptions(BuildContext context, WidgetRef ref) { - final colorScheme = Theme.of(context).colorScheme; - final settings = ref.read(settingsProvider); - final isSafMode = - settings.storageMode == 'saf' && settings.downloadTreeUri.isNotEmpty; - showModalBottomSheet( - context: context, - useRootNavigator: true, - backgroundColor: colorScheme.surfaceContainerHigh, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical(top: Radius.circular(28)), - ), - builder: (ctx) => SafeArea( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.fromLTRB(24, 24, 24, 8), - child: Text( - context.l10n.setupDownloadLocationTitle, - style: Theme.of( - ctx, - ).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold), - ), - ), - Padding( - padding: const EdgeInsets.fromLTRB(24, 0, 24, 16), - child: Text( - context.l10n.downloadLocationSubtitle, - style: Theme.of(ctx).textTheme.bodyMedium?.copyWith( - color: colorScheme.onSurfaceVariant, - ), - ), - ), - ListTile( - leading: Icon(Icons.folder_special, color: colorScheme.primary), - title: Text(context.l10n.storageModeAppFolder), - subtitle: Text(context.l10n.storageModeAppFolderSubtitle), - trailing: !isSafMode ? const Icon(Icons.check) : null, - onTap: () async { - Navigator.pop(ctx); - final defaultDir = await _getDefaultAndroidDirectory(); - final notifier = ref.read(settingsProvider.notifier); - notifier.setStorageMode('app'); - notifier.setDownloadDirectory(defaultDir); - notifier.setDownloadTreeUri(''); - }, - ), - ListTile( - leading: Icon(Icons.folder_open, color: colorScheme.primary), - title: Text(context.l10n.storageModeSaf), - subtitle: Text(context.l10n.storageModeSafSubtitle), - trailing: isSafMode ? const Icon(Icons.check) : null, - onTap: () async { - Navigator.pop(ctx); - final result = await PlatformBridge.pickSafTree(); - if (result != null) { - final treeUri = result['tree_uri'] as String? ?? ''; - final displayName = result['display_name'] as String? ?? ''; - if (treeUri.isNotEmpty) { - ref.read(settingsProvider.notifier).setStorageMode('saf'); - ref - .read(settingsProvider.notifier) - .setDownloadTreeUri( - treeUri, - displayName: displayName.isNotEmpty - ? displayName - : treeUri, - ); - } - } - }, - ), - const SizedBox(height: 8), - ], - ), - ), - ); - } - - void _showIOSDirectoryOptions(BuildContext context, WidgetRef ref) { - final colorScheme = Theme.of(context).colorScheme; - showModalBottomSheet( - context: context, - useRootNavigator: true, - backgroundColor: colorScheme.surfaceContainerHigh, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical(top: Radius.circular(28)), - ), - builder: (ctx) => SafeArea( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.fromLTRB(24, 24, 24, 8), - child: Text( - context.l10n.setupDownloadLocationTitle, - style: Theme.of( - context, - ).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold), - ), - ), - Padding( - padding: const EdgeInsets.fromLTRB(24, 0, 24, 16), - child: Text( - context.l10n.setupDownloadLocationIosMessage, - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: colorScheme.onSurfaceVariant, - ), - ), - ), - ListTile( - leading: Icon(Icons.folder_special, color: colorScheme.primary), - title: Text(context.l10n.setupAppDocumentsFolder), - subtitle: Text(context.l10n.setupAppDocumentsFolderSubtitle), - trailing: Icon(Icons.check_circle, color: colorScheme.primary), - onTap: () async { - final dir = await getApplicationDocumentsDirectory(); - ref - .read(settingsProvider.notifier) - .setDownloadDirectory(dir.path); - if (ctx.mounted) Navigator.pop(ctx); - }, - ), - ListTile( - leading: Icon(Icons.cloud, color: colorScheme.onSurfaceVariant), - title: Text(context.l10n.setupChooseFromFiles), - subtitle: Text(context.l10n.setupChooseFromFilesSubtitle), - onTap: () async { - Navigator.pop(ctx); - if (Platform.isIOS) { - await Future.delayed(const Duration(milliseconds: 250)); - } - - // Note: iOS requires folder to have at least one file to be selectable - String? result; - try { - result = await FilePicker.platform.getDirectoryPath(); - } catch (e) { - if (ctx.mounted) { - ScaffoldMessenger.of(ctx).showSnackBar( - SnackBar( - content: Text( - ctx.l10n.snackbarFolderPickerFailed(e.toString()), - ), - backgroundColor: Theme.of(ctx).colorScheme.error, - duration: const Duration(seconds: 4), - ), - ); - } - return; - } - - if (result != null) { - // iOS: Validate the selected path is writable (not iCloud or container root) - if (Platform.isIOS) { - final validation = validateIosPath(result); - if (!validation.isValid) { - if (ctx.mounted) { - ScaffoldMessenger.of(ctx).showSnackBar( - SnackBar( - content: Text( - validation.errorReason ?? - context.l10n.setupIcloudNotSupported, - ), - backgroundColor: Theme.of(ctx).colorScheme.error, - duration: const Duration(seconds: 4), - ), - ); - } - return; - } - } - ref - .read(settingsProvider.notifier) - .setDownloadDirectory(result); - } - }, - ), - Padding( - padding: const EdgeInsets.fromLTRB(24, 8, 24, 16), - child: Container( - padding: const EdgeInsets.all(12), - decoration: BoxDecoration( - color: colorScheme.tertiaryContainer.withValues(alpha: 0.3), - borderRadius: BorderRadius.circular(12), - ), - child: Row( - children: [ - Icon( - Icons.info_outline, - size: 20, - color: colorScheme.tertiary, - ), - const SizedBox(width: 12), - Expanded( - child: Text( - context.l10n.setupIosEmptyFolderWarning, - style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: colorScheme.onTertiaryContainer, - ), - ), - ), - ], - ), - ), - ), - const SizedBox(height: 8), - ], - ), - ), - ); - } - - String _getFolderOrganizationLabel(String value) { - switch (value) { - case 'playlist': - return 'By Playlist'; - case 'artist': - return 'By Artist'; - case 'album': - return 'By Album'; - case 'artist_album': - return 'Artist/Album'; - default: - return 'None'; - } - } - - String _getPlaylistFolderSubtitle(AppSettings settings) { - if (settings.folderOrganization == 'playlist') { - return context.l10n.downloadCreatePlaylistSourceFolderRedundant; - } - if (settings.createPlaylistFolder) { - return context.l10n.downloadCreatePlaylistSourceFolderEnabled; - } - return context.l10n.downloadCreatePlaylistSourceFolderDisabled; - } - - String _getArtistFolderFilterSubtitle( - BuildContext context, { - required bool usePrimaryArtistOnly, - required bool filterAlbumArtistContributors, - }) { - final statuses = [ - usePrimaryArtistOnly ? 'Primary only: On' : 'Primary only: Off', - filterAlbumArtistContributors - ? 'Album Artist metadata: Primary only' - : 'Album Artist metadata: Full', - ]; - return statuses.join(' | '); - } - - String _getLyricsModeLabel(BuildContext context, String mode) { - switch (mode) { - case 'external': - return context.l10n.lyricsModeExternal; - case 'both': - return context.l10n.lyricsModeBoth; - default: - return context.l10n.lyricsModeEmbed; - } - } - - String _getSongLinkRegionLabel(String code) { - final normalized = code.trim().toUpperCase(); - final effective = normalized.isEmpty ? 'US' : normalized; - final name = _songLinkRegionNames[effective]; - if (name == null) return effective; - return '$effective - $name'; - } - - void _showLyricsModePicker( - BuildContext context, - WidgetRef ref, - String current, - ) { - final colorScheme = Theme.of(context).colorScheme; - showModalBottomSheet( - context: context, - useRootNavigator: true, - backgroundColor: colorScheme.surfaceContainerHigh, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical(top: Radius.circular(28)), - ), - builder: (context) => SafeArea( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.fromLTRB(24, 24, 24, 8), - child: Text( - context.l10n.lyricsMode, - style: Theme.of( - context, - ).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold), - ), - ), - Padding( - padding: const EdgeInsets.fromLTRB(24, 0, 24, 16), - child: Text( - context.l10n.lyricsModeDescription, - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: colorScheme.onSurfaceVariant, - ), - ), - ), - ListTile( - leading: const Icon(Icons.audiotrack), - title: Text(context.l10n.lyricsModeEmbed), - subtitle: Text(context.l10n.lyricsModeEmbedSubtitle), - trailing: current == 'embed' ? const Icon(Icons.check) : null, - onTap: () { - ref.read(settingsProvider.notifier).setLyricsMode('embed'); - Navigator.pop(context); - }, - ), - ListTile( - leading: const Icon(Icons.insert_drive_file_outlined), - title: Text(context.l10n.lyricsModeExternal), - subtitle: Text(context.l10n.lyricsModeExternalSubtitle), - trailing: current == 'external' ? const Icon(Icons.check) : null, - onTap: () { - ref.read(settingsProvider.notifier).setLyricsMode('external'); - Navigator.pop(context); - }, - ), - ListTile( - leading: const Icon(Icons.library_music_outlined), - title: Text(context.l10n.lyricsModeBoth), - subtitle: Text(context.l10n.lyricsModeBothSubtitle), - trailing: current == 'both' ? const Icon(Icons.check) : null, - onTap: () { - ref.read(settingsProvider.notifier).setLyricsMode('both'); - Navigator.pop(context); - }, - ), - const SizedBox(height: 16), - ], - ), - ), - ); - } - - static const _providerDisplayNames = { - 'lrclib': 'LRCLIB', - 'netease': 'Netease', - 'musixmatch': 'Musixmatch', - 'apple_music': 'Apple Music', - 'qqmusic': 'QQ Music', - }; - - String _getLyricsProvidersSubtitle(List providers) { - if (providers.isEmpty) return context.l10n.downloadProvidersNoneEnabled; - return providers.map((p) => _providerDisplayNames[p] ?? p).join(' > '); - } - - String _normalizeMusixmatchLanguage(String value) { - final normalized = value.trim().toLowerCase(); - return normalized.replaceAll(RegExp(r'[^a-z0-9\-_]'), ''); - } - String _getTidalHighFormatLabel(BuildContext context, String format) { switch (format) { case 'mp3_320': @@ -1619,6 +340,19 @@ class _DownloadSettingsPageState extends ConsumerState { } } + String _getSongLinkRegionLabel(String code) { + const names = { + 'US': 'United States', 'GB': 'United Kingdom', 'FR': 'France', + 'DE': 'Germany', 'JP': 'Japan', 'KR': 'South Korea', + 'IN': 'India', 'ID': 'Indonesia', 'BR': 'Brazil', + 'MX': 'Mexico', 'AU': 'Australia', 'CA': 'Canada', 'XK': 'Kosovo', + }; + final normalized = code.trim().toUpperCase(); + final effective = normalized.isEmpty ? 'US' : normalized; + final name = names[effective]; + return name == null ? effective : '$effective - $name'; + } + void _showTidalHighFormatPicker( BuildContext context, WidgetRef ref, @@ -1641,18 +375,16 @@ class _DownloadSettingsPageState extends ConsumerState { padding: const EdgeInsets.fromLTRB(24, 24, 24, 8), child: Text( context.l10n.downloadLossy320Format, - style: Theme.of( - context, - ).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold), + style: Theme.of(context).textTheme.titleLarge + ?.copyWith(fontWeight: FontWeight.bold), ), ), Padding( padding: const EdgeInsets.fromLTRB(24, 0, 24, 16), child: Text( context.l10n.downloadLossy320FormatDesc, - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: colorScheme.onSurfaceVariant, - ), + style: Theme.of(context).textTheme.bodyMedium + ?.copyWith(color: colorScheme.onSurfaceVariant), ), ), ListTile( @@ -1663,9 +395,7 @@ class _DownloadSettingsPageState extends ConsumerState { ? Icon(Icons.check, color: colorScheme.primary) : null, onTap: () { - ref - .read(settingsProvider.notifier) - .setTidalHighFormat('mp3_320'); + ref.read(settingsProvider.notifier).setTidalHighFormat('mp3_320'); Navigator.pop(context); }, ), @@ -1677,9 +407,7 @@ class _DownloadSettingsPageState extends ConsumerState { ? Icon(Icons.check, color: colorScheme.primary) : null, onTap: () { - ref - .read(settingsProvider.notifier) - .setTidalHighFormat('opus_256'); + ref.read(settingsProvider.notifier).setTidalHighFormat('opus_256'); Navigator.pop(context); }, ), @@ -1691,9 +419,7 @@ class _DownloadSettingsPageState extends ConsumerState { ? Icon(Icons.check, color: colorScheme.primary) : null, onTap: () { - ref - .read(settingsProvider.notifier) - .setTidalHighFormat('opus_128'); + ref.read(settingsProvider.notifier).setTidalHighFormat('opus_128'); Navigator.pop(context); }, ), @@ -1704,94 +430,6 @@ class _DownloadSettingsPageState extends ConsumerState { ); } - void _showMusixmatchLanguagePicker( - BuildContext context, - WidgetRef ref, - String currentLanguage, - ) { - final colorScheme = Theme.of(context).colorScheme; - final controller = TextEditingController(text: currentLanguage); - - showModalBottomSheet( - context: context, - useRootNavigator: true, - backgroundColor: colorScheme.surfaceContainerHigh, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical(top: Radius.circular(28)), - ), - isScrollControlled: true, - builder: (context) => Padding( - padding: EdgeInsets.only( - left: 24, - right: 24, - top: 24, - bottom: 24 + MediaQuery.of(context).viewInsets.bottom, - ), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - context.l10n.downloadMusixmatchLanguage, - style: Theme.of( - context, - ).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold), - ), - const SizedBox(height: 8), - Text( - context.l10n.downloadMusixmatchLanguageDesc, - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: colorScheme.onSurfaceVariant, - ), - ), - const SizedBox(height: 16), - TextField( - controller: controller, - textInputAction: TextInputAction.done, - decoration: InputDecoration( - labelText: context.l10n.downloadMusixmatchLanguageCode, - hintText: context.l10n.downloadMusixmatchLanguageHint, - ), - ), - const SizedBox(height: 16), - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - TextButton( - onPressed: () => Navigator.pop(context), - child: Text(context.l10n.dialogCancel), - ), - const SizedBox(width: 8), - TextButton( - onPressed: () { - ref - .read(settingsProvider.notifier) - .setMusixmatchLanguage(''); - Navigator.pop(context); - }, - child: Text(context.l10n.downloadMusixmatchAuto), - ), - const SizedBox(width: 8), - FilledButton( - onPressed: () { - final normalized = _normalizeMusixmatchLanguage( - controller.text, - ); - ref - .read(settingsProvider.notifier) - .setMusixmatchLanguage(normalized); - Navigator.pop(context); - }, - child: Text(context.l10n.dialogSave), - ), - ], - ), - ], - ), - ), - ); - } - void _showNetworkModePicker( BuildContext context, WidgetRef ref, @@ -1814,18 +452,16 @@ class _DownloadSettingsPageState extends ConsumerState { padding: const EdgeInsets.fromLTRB(24, 24, 24, 8), child: Text( context.l10n.settingsDownloadNetwork, - style: Theme.of( - context, - ).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold), + style: Theme.of(context).textTheme.titleLarge + ?.copyWith(fontWeight: FontWeight.bold), ), ), Padding( padding: const EdgeInsets.fromLTRB(24, 0, 24, 16), child: Text( context.l10n.settingsDownloadNetworkSubtitle, - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: colorScheme.onSurfaceVariant, - ), + style: Theme.of(context).textTheme.bodyMedium + ?.copyWith(color: colorScheme.onSurfaceVariant), ), ), ListTile( @@ -1836,9 +472,7 @@ class _DownloadSettingsPageState extends ConsumerState { ? Icon(Icons.check, color: colorScheme.primary) : null, onTap: () { - ref - .read(settingsProvider.notifier) - .setDownloadNetworkMode('any'); + ref.read(settingsProvider.notifier).setDownloadNetworkMode('any'); Navigator.pop(context); }, ), @@ -1868,6 +502,27 @@ class _DownloadSettingsPageState extends ConsumerState { WidgetRef ref, String current, ) { + const regions = [ + 'AD','AE','AG','AL','AM','AO','AR','AT','AU','AZ','BA','BB','BD','BE', + 'BF','BG','BH','BI','BJ','BN','BO','BR','BS','BT','BW','BZ','CA','CD', + 'CG','CH','CI','CL','CM','CO','CR','CV','CW','CY','CZ','DE','DJ','DK', + 'DM','DO','DZ','EC','EE','EG','ES','ET','FI','FJ','FM','FR','GA','GB', + 'GD','GE','GH','GM','GN','GQ','GR','GT','GW','GY','HK','HN','HR','HT', + 'HU','ID','IE','IL','IN','IQ','IS','IT','JM','JO','JP','KE','KG','KH', + 'KI','KM','KN','KR','KW','KZ','LA','LB','LC','LI','LK','LR','LS','LT', + 'LU','LV','LY','MA','MC','MD','ME','MG','MH','MK','ML','MN','MO','MR', + 'MT','MU','MV','MW','MX','MY','MZ','NA','NE','NG','NI','NL','NO','NP', + 'NR','NZ','OM','PA','PE','PG','PH','PK','PL','PS','PT','PW','PY','QA', + 'RO','RS','RW','SA','SB','SC','SE','SG','SI','SK','SL','SM','SN','SR', + 'ST','SV','SZ','TD','TG','TH','TJ','TL','TN','TO','TR','TT','TV','TW', + 'TZ','UA','UG','US','UY','UZ','VC','VE','VN','VU','WS','XK','ZA','ZM','ZW', + ]; + const names = { + 'US': 'United States', 'GB': 'United Kingdom', 'FR': 'France', + 'DE': 'Germany', 'JP': 'Japan', 'KR': 'South Korea', + 'IN': 'India', 'ID': 'Indonesia', 'BR': 'Brazil', + 'MX': 'Mexico', 'AU': 'Australia', 'CA': 'Canada', 'XK': 'Kosovo', + }; final colorScheme = Theme.of(context).colorScheme; final normalizedCurrent = current.trim().toUpperCase(); showModalBottomSheet( @@ -1888,30 +543,27 @@ class _DownloadSettingsPageState extends ConsumerState { padding: const EdgeInsets.fromLTRB(24, 24, 24, 8), child: Text( context.l10n.downloadSongLinkRegion, - style: Theme.of( - context, - ).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold), + style: Theme.of(context).textTheme.titleLarge + ?.copyWith(fontWeight: FontWeight.bold), ), ), Padding( padding: const EdgeInsets.fromLTRB(24, 0, 24, 16), child: Text( context.l10n.downloadSongLinkRegionDesc, - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: colorScheme.onSurfaceVariant, - ), + style: Theme.of(context).textTheme.bodyMedium + ?.copyWith(color: colorScheme.onSurfaceVariant), ), ), Expanded( child: ListView.builder( - itemCount: _songLinkRegions.length, + itemCount: regions.length, itemBuilder: (context, index) { - final code = _songLinkRegions[index]; + final code = regions[index]; final isSelected = code == normalizedCurrent; - final displayName = _songLinkRegionNames[code]; return ListTile( title: Text(code), - subtitle: displayName != null ? Text(displayName) : null, + subtitle: names[code] != null ? Text(names[code]!) : null, trailing: isSelected ? Icon(Icons.check, color: colorScheme.primary) : null, @@ -1931,117 +583,10 @@ class _DownloadSettingsPageState extends ConsumerState { ), ); } - - void _showFolderOrganizationPicker( - BuildContext context, - WidgetRef ref, - String current, - ) { - final colorScheme = Theme.of(context).colorScheme; - showModalBottomSheet( - context: context, - useRootNavigator: true, - backgroundColor: colorScheme.surfaceContainerHigh, - isScrollControlled: true, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical(top: Radius.circular(28)), - ), - constraints: BoxConstraints( - maxHeight: MediaQuery.of(context).size.height * 0.7, - ), - builder: (context) => SafeArea( - child: SingleChildScrollView( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.fromLTRB(24, 24, 24, 8), - child: Text( - context.l10n.downloadFolderOrganization, - style: Theme.of( - context, - ).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold), - ), - ), - Padding( - padding: const EdgeInsets.fromLTRB(24, 0, 24, 16), - child: Text( - context.l10n.folderOrganizationDescription, - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: colorScheme.onSurfaceVariant, - ), - ), - ), - _FolderOption( - title: context.l10n.folderOrganizationNone, - subtitle: context.l10n.folderOrganizationNoneSubtitle, - example: 'SpotiFLAC/Track.flac', - isSelected: current == 'none', - onTap: () { - ref - .read(settingsProvider.notifier) - .setFolderOrganization('none'); - Navigator.pop(context); - }, - ), - _FolderOption( - title: context.l10n.folderOrganizationByPlaylist, - subtitle: context.l10n.folderOrganizationByPlaylistSubtitle, - example: 'SpotiFLAC/Playlist Name/Track.flac', - isSelected: current == 'playlist', - onTap: () { - ref - .read(settingsProvider.notifier) - .setFolderOrganization('playlist'); - Navigator.pop(context); - }, - ), - _FolderOption( - title: context.l10n.folderOrganizationByArtist, - subtitle: context.l10n.folderOrganizationByArtistSubtitle, - example: 'SpotiFLAC/Artist Name/Track.flac', - isSelected: current == 'artist', - onTap: () { - ref - .read(settingsProvider.notifier) - .setFolderOrganization('artist'); - Navigator.pop(context); - }, - ), - _FolderOption( - title: context.l10n.folderOrganizationByAlbum, - subtitle: context.l10n.folderOrganizationByAlbumSubtitle, - example: 'SpotiFLAC/Album Name/Track.flac', - isSelected: current == 'album', - onTap: () { - ref - .read(settingsProvider.notifier) - .setFolderOrganization('album'); - Navigator.pop(context); - }, - ), - _FolderOption( - title: context.l10n.folderOrganizationByArtistAlbum, - subtitle: context.l10n.folderOrganizationByArtistAlbumSubtitle, - example: 'SpotiFLAC/Artist/Album/Track.flac', - isSelected: current == 'artist_album', - onTap: () { - ref - .read(settingsProvider.notifier) - .setFolderOrganization('artist_album'); - Navigator.pop(context); - }, - ), - const SizedBox(height: 16), - ], - ), - ), - ), - ); - } } +// ── Private widgets (reused from original) ───────────────────────────────── + class _ServiceSelector extends ConsumerWidget { final String currentService; final ValueChanged onChanged; @@ -2129,14 +674,12 @@ class _ServiceChip extends StatelessWidget { Widget build(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; final isDark = Theme.of(context).brightness == Brightness.dark; - final unselectedColor = isDark ? Color.alphaBlend( Colors.white.withValues(alpha: 0.05), colorScheme.surface, ) : colorScheme.surfaceContainerHigh; - return Material( color: isSelected ? colorScheme.primaryContainer : unselectedColor, borderRadius: BorderRadius.circular(12), @@ -2234,16 +777,91 @@ class _QualityOption extends StatelessWidget { } } -class _FolderOption extends StatelessWidget { - final String title; - final String subtitle; - final String example; +class _ConcurrentDownloadsItem extends StatelessWidget { + final int currentValue; + final ValueChanged onChanged; + const _ConcurrentDownloadsItem({ + required this.currentValue, + required this.onChanged, + }); + + @override + Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return Padding( + padding: const EdgeInsets.all(20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Icon( + Icons.download_for_offline, + color: colorScheme.onSurfaceVariant, + size: 24, + ), + const SizedBox(width: 16), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + context.l10n.optionsConcurrentDownloads, + style: Theme.of(context).textTheme.bodyLarge, + ), + const SizedBox(height: 2), + Text( + currentValue == 1 + ? context.l10n.optionsConcurrentSequential + : context.l10n.optionsConcurrentParallel(currentValue), + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + color: colorScheme.onSurfaceVariant, + ), + ), + ], + ), + ), + ], + ), + const SizedBox(height: 16), + Row( + children: [ + for (final n in [1, 2, 3, 4, 5]) ...[ + if (n > 1) const SizedBox(width: 8), + _ConcurrentChip( + label: '$n', + isSelected: currentValue == n, + onTap: () => onChanged(n), + ), + ], + ], + ), + const SizedBox(height: 12), + Row( + children: [ + Icon(Icons.warning_amber_rounded, size: 16, color: colorScheme.error), + const SizedBox(width: 8), + Expanded( + child: Text( + context.l10n.optionsConcurrentWarning, + style: Theme.of(context).textTheme.bodySmall + ?.copyWith(color: colorScheme.error), + ), + ), + ], + ), + ], + ), + ); + } +} + +class _ConcurrentChip extends StatelessWidget { + final String label; final bool isSelected; final VoidCallback onTap; - const _FolderOption({ - required this.title, - required this.subtitle, - required this.example, + const _ConcurrentChip({ + required this.label, required this.isSelected, required this.onTap, }); @@ -2251,28 +869,204 @@ class _FolderOption extends StatelessWidget { @override Widget build(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; - return ListTile( - contentPadding: const EdgeInsets.symmetric(horizontal: 24, vertical: 4), - title: Text(title), - subtitle: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(subtitle), - const SizedBox(height: 4), - Text( - example, - style: TextStyle( - fontFamily: 'monospace', - fontSize: 11, - color: colorScheme.primary, + final isDark = Theme.of(context).brightness == Brightness.dark; + final unselectedColor = isDark + ? Color.alphaBlend( + Colors.white.withValues(alpha: 0.05), + colorScheme.surface, + ) + : colorScheme.surfaceContainerHigh; + return Expanded( + child: Material( + color: isSelected ? colorScheme.primaryContainer : unselectedColor, + borderRadius: BorderRadius.circular(12), + child: InkWell( + onTap: onTap, + borderRadius: BorderRadius.circular(12), + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 12), + child: Center( + child: Text( + label, + style: TextStyle( + fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal, + color: isSelected + ? colorScheme.onPrimaryContainer + : colorScheme.onSurfaceVariant, + ), + ), ), ), - ], + ), ), - trailing: isSelected - ? Icon(Icons.check_circle, color: colorScheme.primary) - : Icon(Icons.circle_outlined, color: colorScheme.outline), - onTap: onTap, + ); + } +} + +// Imported from options_settings_page — search source selectors +class _MetadataSourceSelector extends ConsumerWidget { + const _MetadataSourceSelector(); + + static const _builtInProviders = {'tidal': 'Tidal', 'qobuz': 'Qobuz'}; + + Extension? _defaultSearchExtension(List extensions) { + return extensions + .where( + (ext) => + ext.enabled && + ext.hasCustomSearch && + ext.searchBehavior?.primary == true, + ) + .firstOrNull ?? + extensions + .where((ext) => ext.enabled && ext.hasCustomSearch) + .firstOrNull; + } + + @override + Widget build(BuildContext context, WidgetRef ref) { + final colorScheme = Theme.of(context).colorScheme; + final settings = ref.watch(settingsProvider); + final extState = ref.watch(extensionProvider); + + final rawSearchProvider = settings.searchProvider?.trim() ?? ''; + final isValidBuiltIn = _builtInProviders.containsKey(rawSearchProvider); + final primarySearchExtension = _defaultSearchExtension(extState.extensions); + final defaultProviderTarget = + primarySearchExtension?.displayName ?? 'Tidal'; + final defaultProviderLabel = + '${context.l10n.extensionsHomeFeedAuto} ($defaultProviderTarget)'; + final searchProvider = + isValidBuiltIn || + extState.extensions.any( + (e) => + e.enabled && e.hasCustomSearch && e.id == rawSearchProvider, + ) + ? rawSearchProvider + : ''; + final isBuiltIn = _builtInProviders.containsKey(searchProvider); + + Extension? activeExtension; + if (searchProvider.isNotEmpty && !isBuiltIn) { + activeExtension = extState.extensions + .where((e) => e.id == searchProvider && e.enabled) + .firstOrNull; + } + final hasNonDefaultProvider = isBuiltIn || activeExtension != null; + + String subtitle; + if (isBuiltIn) { + subtitle = 'Using ${_builtInProviders[searchProvider]}'; + } else if (activeExtension != null) { + subtitle = context.l10n.optionsUsingExtension(activeExtension.displayName); + } else { + subtitle = context.l10n.optionsPrimaryProviderSubtitle; + } + + return Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + context.l10n.optionsPrimaryProvider, + style: Theme.of(context).textTheme.titleMedium + ?.copyWith(fontWeight: FontWeight.w500), + ), + const SizedBox(height: 4), + Text( + subtitle, + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + color: hasNonDefaultProvider + ? colorScheme.primary + : colorScheme.onSurfaceVariant, + ), + ), + const SizedBox(height: 12), + Wrap( + spacing: 8, + runSpacing: 8, + children: [ + _SearchProviderChip( + label: defaultProviderLabel, + isSelected: searchProvider.isEmpty, + onTap: () => ref + .read(settingsProvider.notifier) + .setSearchProvider(''), + ), + for (final entry in _builtInProviders.entries) + _SearchProviderChip( + label: entry.value, + isSelected: searchProvider == entry.key, + onTap: () => ref + .read(settingsProvider.notifier) + .setSearchProvider(entry.key), + ), + for (final ext in extState.extensions.where( + (e) => e.enabled && e.hasCustomSearch, + )) + _SearchProviderChip( + label: ext.displayName, + isSelected: searchProvider == ext.id, + onTap: () => ref + .read(settingsProvider.notifier) + .setSearchProvider(ext.id), + ), + ], + ), + ], + ), + ); + } +} + +class _SearchProviderChip extends StatelessWidget { + final String label; + final bool isSelected; + final VoidCallback onTap; + const _SearchProviderChip({ + required this.label, + required this.isSelected, + required this.onTap, + }); + + @override + Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return FilterChip( + label: Text(label), + selected: isSelected, + onSelected: (_) => onTap(), + selectedColor: colorScheme.primaryContainer, + checkmarkColor: colorScheme.onPrimaryContainer, + labelStyle: TextStyle( + color: isSelected + ? colorScheme.onPrimaryContainer + : colorScheme.onSurface, + ), + ); + } +} + +class _DefaultSearchTabSelector extends ConsumerWidget { + const _DefaultSearchTabSelector(); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final settings = ref.watch(settingsProvider); + final colorScheme = Theme.of(context).colorScheme; + return SettingsItem( + icon: Icons.tab_outlined, + title: context.l10n.optionsDefaultSearchTab, + subtitle: settings.defaultSearchTab == 'albums' + ? context.l10n.optionsDefaultSearchTabAlbums + : context.l10n.optionsDefaultSearchTabTracks, + onTap: () { + final current = settings.defaultSearchTab; + ref.read(settingsProvider.notifier).setDefaultSearchTab( + current == 'albums' ? 'tracks' : 'albums', + ); + }, ); } } diff --git a/lib/screens/settings/files_settings_page.dart b/lib/screens/settings/files_settings_page.dart new file mode 100644 index 00000000..0382abc4 --- /dev/null +++ b/lib/screens/settings/files_settings_page.dart @@ -0,0 +1,1055 @@ +import 'dart:io'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:file_picker/file_picker.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:permission_handler/permission_handler.dart'; +import 'package:device_info_plus/device_info_plus.dart'; +import 'package:spotiflac_android/l10n/l10n.dart'; +import 'package:spotiflac_android/models/settings.dart'; +import 'package:spotiflac_android/providers/settings_provider.dart'; +import 'package:spotiflac_android/services/platform_bridge.dart'; +import 'package:spotiflac_android/utils/app_bar_layout.dart'; +import 'package:spotiflac_android/utils/file_access.dart'; +import 'package:spotiflac_android/widgets/settings_group.dart'; + +class FilesSettingsPage extends ConsumerStatefulWidget { + const FilesSettingsPage({super.key}); + + @override + ConsumerState createState() => _FilesSettingsPageState(); +} + +class _FilesSettingsPageState extends ConsumerState { + int _androidSdkVersion = 0; + bool _hasAllFilesAccess = false; + bool _artistFolderFiltersExpanded = false; + + @override + void initState() { + super.initState(); + _initDeviceInfo(); + } + + Future _initDeviceInfo() async { + if (Platform.isAndroid) { + final deviceInfo = DeviceInfoPlugin(); + final androidInfo = await deviceInfo.androidInfo; + final sdkVersion = androidInfo.version.sdkInt; + final hasAccess = await Permission.manageExternalStorage.isGranted; + if (mounted) { + setState(() { + _androidSdkVersion = sdkVersion; + _hasAllFilesAccess = hasAccess; + }); + } + } + } + + Future _requestAllFilesAccess() async { + final status = await Permission.manageExternalStorage.request(); + if (status.isGranted) { + ref.read(settingsProvider.notifier).setUseAllFilesAccess(true); + if (mounted) setState(() => _hasAllFilesAccess = true); + } else if (status.isPermanentlyDenied) { + if (mounted) { + final shouldOpen = await showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text(context.l10n.setupStorageAccessRequired), + content: Text(context.l10n.allFilesAccessDeniedMessage), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context, false), + child: Text(context.l10n.dialogCancel), + ), + FilledButton( + onPressed: () => Navigator.pop(context, true), + child: Text(context.l10n.setupOpenSettings), + ), + ], + ), + ); + if (shouldOpen == true) await openAppSettings(); + } + } + } + + Future _disableAllFilesAccess() async { + ref.read(settingsProvider.notifier).setUseAllFilesAccess(false); + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(context.l10n.allFilesAccessDisabledMessage)), + ); + } + } + + @override + Widget build(BuildContext context) { + final settings = ref.watch(settingsProvider); + final colorScheme = Theme.of(context).colorScheme; + final topPadding = normalizedHeaderTopPadding(context); + + return PopScope( + canPop: true, + child: Scaffold( + body: CustomScrollView( + slivers: [ + SliverAppBar( + expandedHeight: 120 + topPadding, + collapsedHeight: kToolbarHeight, + floating: false, + pinned: true, + backgroundColor: colorScheme.surface, + surfaceTintColor: Colors.transparent, + leading: IconButton( + tooltip: MaterialLocalizations.of(context).backButtonTooltip, + icon: const Icon(Icons.arrow_back), + onPressed: () => Navigator.pop(context), + ), + flexibleSpace: LayoutBuilder( + builder: (context, constraints) { + final maxHeight = 120 + topPadding; + final minHeight = kToolbarHeight + topPadding; + final expandRatio = + ((constraints.maxHeight - minHeight) / + (maxHeight - minHeight)) + .clamp(0.0, 1.0); + final leftPadding = 56 - (32 * expandRatio); + return FlexibleSpaceBar( + expandedTitleScale: 1.0, + titlePadding: EdgeInsets.only( + left: leftPadding, + bottom: 16, + ), + title: Text( + context.l10n.settingsFiles, + style: TextStyle( + fontSize: 20 + (8 * expandRatio), + fontWeight: FontWeight.bold, + color: colorScheme.onSurface, + ), + ), + ); + }, + ), + ), + + // ── Download Location ────────────────────────────────────── + SliverToBoxAdapter( + child: SettingsSectionHeader( + title: context.l10n.setupDownloadLocationTitle, + ), + ), + SliverToBoxAdapter( + child: SettingsGroup( + children: [ + SettingsItem( + icon: Icons.folder_outlined, + title: context.l10n.downloadDirectory, + subtitle: settings.downloadDirectory.isEmpty + ? (Platform.isIOS + ? context.l10n.setupAppDocumentsFolder + : 'Music/SpotiFLAC') + : settings.downloadDirectory, + onTap: () => _pickDirectory(context, ref), + showDivider: false, + ), + ], + ), + ), + + // ── Filename Formats ─────────────────────────────────────── + SliverToBoxAdapter( + child: SettingsSectionHeader( + title: context.l10n.sectionFileSettings, + ), + ), + SliverToBoxAdapter( + child: SettingsGroup( + children: [ + SettingsItem( + icon: Icons.text_fields, + title: context.l10n.downloadFilenameFormat, + subtitle: settings.filenameFormat, + onTap: () => _showFormatEditor( + context, + ref, + settings.filenameFormat, + ), + ), + SettingsItem( + icon: Icons.music_note_outlined, + title: context.l10n.downloadSingleFilenameFormat, + subtitle: settings.singleFilenameFormat, + onTap: () => _showFormatEditor( + context, + ref, + settings.singleFilenameFormat, + onSave: ref + .read(settingsProvider.notifier) + .setSingleFilenameFormat, + title: context.l10n.downloadSingleFilenameFormat, + description: + context.l10n.downloadSingleFilenameFormatDescription, + ), + showDivider: false, + ), + ], + ), + ), + + // ── Folder Structure ─────────────────────────────────────── + SliverToBoxAdapter( + child: SettingsSectionHeader( + title: context.l10n.downloadFolderOrganization, + ), + ), + SliverToBoxAdapter( + child: SettingsGroup( + children: [ + SettingsSwitchItem( + icon: Icons.library_music_outlined, + title: context.l10n.downloadSeparateSinglesFolder, + subtitle: settings.separateSingles + ? context.l10n.downloadSeparateSinglesEnabled + : context.l10n.downloadSeparateSinglesDisabled, + value: settings.separateSingles, + onChanged: (value) => ref + .read(settingsProvider.notifier) + .setSeparateSingles(value), + ), + if (settings.separateSingles) + SettingsItem( + icon: Icons.folder_outlined, + title: context.l10n.downloadAlbumFolderStructure, + subtitle: _getAlbumFolderStructureLabel( + settings.albumFolderStructure, + ), + onTap: () => _showAlbumFolderStructurePicker( + context, + ref, + settings.albumFolderStructure, + ), + ), + if (!settings.separateSingles) + SettingsItem( + icon: Icons.create_new_folder_outlined, + title: context.l10n.downloadFolderOrganization, + subtitle: _getFolderOrganizationLabel( + settings.folderOrganization, + ), + onTap: () => _showFolderOrganizationPicker( + context, + ref, + settings.folderOrganization, + ), + ), + SettingsSwitchItem( + icon: Icons.playlist_play_outlined, + title: context.l10n.downloadCreatePlaylistSourceFolder, + subtitle: _getPlaylistFolderSubtitle(settings), + value: settings.createPlaylistFolder, + onChanged: (value) => ref + .read(settingsProvider.notifier) + .setCreatePlaylistFolder(value), + ), + SettingsSwitchItem( + icon: Icons.person_search_outlined, + title: context.l10n.downloadUseAlbumArtistForFolders, + subtitle: settings.useAlbumArtistForFolders + ? context + .l10n + .downloadUseAlbumArtistForFoldersAlbumSubtitle + : context + .l10n + .downloadUseAlbumArtistForFoldersTrackSubtitle, + value: settings.useAlbumArtistForFolders, + onChanged: (value) => ref + .read(settingsProvider.notifier) + .setUseAlbumArtistForFolders(value), + ), + SettingsItem( + icon: Icons.filter_alt_outlined, + title: context.l10n.downloadArtistNameFilters, + subtitle: _getArtistFolderFilterSubtitle( + context, + usePrimaryArtistOnly: settings.usePrimaryArtistOnly, + filterAlbumArtistContributors: + settings.filterContributingArtistsInAlbumArtist, + ), + trailing: Icon( + _artistFolderFiltersExpanded + ? Icons.expand_less + : Icons.expand_more, + ), + onTap: () => setState(() { + _artistFolderFiltersExpanded = + !_artistFolderFiltersExpanded; + }), + showDivider: !_artistFolderFiltersExpanded, + ), + if (_artistFolderFiltersExpanded) ...[ + SettingsSwitchItem( + icon: Icons.person_outline, + title: context.l10n.downloadUsePrimaryArtistOnly, + subtitle: settings.usePrimaryArtistOnly + ? context.l10n.downloadUsePrimaryArtistOnlyEnabled + : context.l10n.downloadUsePrimaryArtistOnlyDisabled, + value: settings.usePrimaryArtistOnly, + onChanged: (value) => ref + .read(settingsProvider.notifier) + .setUsePrimaryArtistOnly(value), + ), + SettingsSwitchItem( + icon: Icons.group_remove_outlined, + title: context.l10n.downloadFilterContributing, + subtitle: settings.filterContributingArtistsInAlbumArtist + ? context.l10n.downloadFilterContributingEnabled + : context.l10n.downloadFilterContributingDisabled, + value: settings.filterContributingArtistsInAlbumArtist, + onChanged: (value) => ref + .read(settingsProvider.notifier) + .setFilterContributingArtistsInAlbumArtist(value), + showDivider: false, + ), + ], + ], + ), + ), + + // ── Storage Access (Android 13+) ─────────────────────────── + if (Platform.isAndroid && _androidSdkVersion >= 33) ...[ + SliverToBoxAdapter( + child: SettingsSectionHeader( + title: context.l10n.sectionStorageAccess, + ), + ), + SliverToBoxAdapter( + child: SettingsGroup( + children: [ + SettingsSwitchItem( + icon: Icons.folder_special_outlined, + title: context.l10n.allFilesAccess, + subtitle: _hasAllFilesAccess + ? context.l10n.allFilesAccessEnabledSubtitle + : context.l10n.allFilesAccessDisabledSubtitle, + value: _hasAllFilesAccess && + settings.useAllFilesAccess, + onChanged: (value) { + if (value) { + _requestAllFilesAccess(); + } else { + _disableAllFilesAccess(); + } + }, + showDivider: false, + ), + ], + ), + ), + SliverToBoxAdapter( + child: Padding( + padding: const EdgeInsets.fromLTRB(16, 8, 16, 0), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Icon( + Icons.info_outline, + size: 16, + color: colorScheme.onSurfaceVariant, + ), + const SizedBox(width: 8), + Expanded( + child: Text( + context.l10n.allFilesAccessDescription, + style: Theme.of(context).textTheme.bodySmall + ?.copyWith(color: colorScheme.onSurfaceVariant), + ), + ), + ], + ), + ), + ), + ], + + const SliverToBoxAdapter(child: SizedBox(height: 32)), + ], + ), + ), + ); + } + + // ── Helpers ────────────────────────────────────────────────────────────── + + String _getAlbumFolderStructureLabel(String structure) { + switch (structure) { + case 'album_only': + return 'Albums/Album Name/'; + case 'artist_year_album': + return 'Albums/Artist/[Year] Album/'; + case 'year_album': + return 'Albums/[Year] Album/'; + case 'artist_album_singles': + return 'Artist/Album/ + Artist/Singles/'; + case 'artist_album_flat': + return 'Artist/Album/ + Artist/song.flac'; + default: + return 'Albums/Artist/Album Name/'; + } + } + + String _getFolderOrganizationLabel(String value) { + switch (value) { + case 'playlist': + return 'By Playlist'; + case 'artist': + return 'By Artist'; + case 'album': + return 'By Album'; + case 'artist_album': + return 'Artist/Album'; + default: + return 'None'; + } + } + + String _getPlaylistFolderSubtitle(AppSettings settings) { + if (settings.folderOrganization == 'playlist') { + return context.l10n.downloadCreatePlaylistSourceFolderRedundant; + } + if (settings.createPlaylistFolder) { + return context.l10n.downloadCreatePlaylistSourceFolderEnabled; + } + return context.l10n.downloadCreatePlaylistSourceFolderDisabled; + } + + String _getArtistFolderFilterSubtitle( + BuildContext context, { + required bool usePrimaryArtistOnly, + required bool filterAlbumArtistContributors, + }) { + final statuses = [ + usePrimaryArtistOnly ? 'Primary only: On' : 'Primary only: Off', + filterAlbumArtistContributors + ? 'Album Artist metadata: Primary only' + : 'Album Artist metadata: Full', + ]; + return statuses.join(' | '); + } + + Future _pickDirectory(BuildContext context, WidgetRef ref) async { + if (Platform.isIOS) { + _showIOSDirectoryOptions(context, ref); + } else if (Platform.isAndroid) { + _showAndroidDirectoryOptions(context, ref); + } + } + + Future _getDefaultAndroidDirectory() async { + const directMusicPath = '/storage/emulated/0/Music/SpotiFLAC'; + try { + final musicDir = Directory(directMusicPath); + if (!await musicDir.exists()) await musicDir.create(recursive: true); + return musicDir.path; + } catch (_) {} + try { + final externalDir = await getExternalStorageDirectory(); + if (externalDir != null) { + final musicDir = Directory( + '${externalDir.parent.parent.parent.parent.path}/Music/SpotiFLAC', + ); + if (!await musicDir.exists()) await musicDir.create(recursive: true); + return musicDir.path; + } + } catch (_) {} + final appDir = await getApplicationDocumentsDirectory(); + final fallbackDir = Directory('${appDir.path}/SpotiFLAC'); + if (!await fallbackDir.exists()) await fallbackDir.create(recursive: true); + return fallbackDir.path; + } + + void _showAndroidDirectoryOptions(BuildContext context, WidgetRef ref) { + final colorScheme = Theme.of(context).colorScheme; + final settings = ref.read(settingsProvider); + final isSafMode = + settings.storageMode == 'saf' && settings.downloadTreeUri.isNotEmpty; + showModalBottomSheet( + context: context, + useRootNavigator: true, + backgroundColor: colorScheme.surfaceContainerHigh, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(28)), + ), + builder: (ctx) => SafeArea( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(24, 24, 24, 8), + child: Text( + context.l10n.setupDownloadLocationTitle, + style: Theme.of(ctx).textTheme.titleLarge + ?.copyWith(fontWeight: FontWeight.bold), + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(24, 0, 24, 16), + child: Text( + context.l10n.downloadLocationSubtitle, + style: Theme.of(ctx).textTheme.bodyMedium + ?.copyWith(color: colorScheme.onSurfaceVariant), + ), + ), + ListTile( + leading: Icon(Icons.folder_special, color: colorScheme.primary), + title: Text(context.l10n.storageModeAppFolder), + subtitle: Text(context.l10n.storageModeAppFolderSubtitle), + trailing: !isSafMode ? const Icon(Icons.check) : null, + onTap: () async { + Navigator.pop(ctx); + final defaultDir = await _getDefaultAndroidDirectory(); + final notifier = ref.read(settingsProvider.notifier); + notifier.setStorageMode('app'); + notifier.setDownloadDirectory(defaultDir); + notifier.setDownloadTreeUri(''); + }, + ), + ListTile( + leading: Icon(Icons.folder_open, color: colorScheme.primary), + title: Text(context.l10n.storageModeSaf), + subtitle: Text(context.l10n.storageModeSafSubtitle), + trailing: isSafMode ? const Icon(Icons.check) : null, + onTap: () async { + Navigator.pop(ctx); + final result = await PlatformBridge.pickSafTree(); + if (result != null) { + final treeUri = result['tree_uri'] as String? ?? ''; + final displayName = result['display_name'] as String? ?? ''; + if (treeUri.isNotEmpty) { + ref.read(settingsProvider.notifier).setStorageMode('saf'); + ref.read(settingsProvider.notifier).setDownloadTreeUri( + treeUri, + displayName: displayName.isNotEmpty ? displayName : treeUri, + ); + } + } + }, + ), + const SizedBox(height: 8), + ], + ), + ), + ); + } + + void _showIOSDirectoryOptions(BuildContext context, WidgetRef ref) { + final colorScheme = Theme.of(context).colorScheme; + showModalBottomSheet( + context: context, + useRootNavigator: true, + backgroundColor: colorScheme.surfaceContainerHigh, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(28)), + ), + builder: (ctx) => SafeArea( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(24, 24, 24, 8), + child: Text( + context.l10n.setupDownloadLocationTitle, + style: Theme.of(context).textTheme.titleLarge + ?.copyWith(fontWeight: FontWeight.bold), + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(24, 0, 24, 16), + child: Text( + context.l10n.setupDownloadLocationIosMessage, + style: Theme.of(context).textTheme.bodyMedium + ?.copyWith(color: colorScheme.onSurfaceVariant), + ), + ), + ListTile( + leading: Icon(Icons.folder_special, color: colorScheme.primary), + title: Text(context.l10n.setupAppDocumentsFolder), + subtitle: Text(context.l10n.setupAppDocumentsFolderSubtitle), + trailing: Icon(Icons.check_circle, color: colorScheme.primary), + onTap: () async { + final dir = await getApplicationDocumentsDirectory(); + ref + .read(settingsProvider.notifier) + .setDownloadDirectory(dir.path); + if (ctx.mounted) Navigator.pop(ctx); + }, + ), + ListTile( + leading: Icon(Icons.cloud, color: colorScheme.onSurfaceVariant), + title: Text(context.l10n.setupChooseFromFiles), + subtitle: Text(context.l10n.setupChooseFromFilesSubtitle), + onTap: () async { + Navigator.pop(ctx); + if (Platform.isIOS) { + await Future.delayed(const Duration(milliseconds: 250)); + } + String? result; + try { + result = await FilePicker.platform.getDirectoryPath(); + } catch (e) { + if (ctx.mounted) { + ScaffoldMessenger.of(ctx).showSnackBar( + SnackBar( + content: Text( + ctx.l10n.snackbarFolderPickerFailed(e.toString()), + ), + backgroundColor: Theme.of(ctx).colorScheme.error, + duration: const Duration(seconds: 4), + ), + ); + } + return; + } + if (result != null) { + if (Platform.isIOS) { + final validation = validateIosPath(result); + if (!validation.isValid) { + if (ctx.mounted) { + ScaffoldMessenger.of(ctx).showSnackBar( + SnackBar( + content: Text( + validation.errorReason ?? + context.l10n.setupIcloudNotSupported, + ), + backgroundColor: Theme.of(ctx).colorScheme.error, + duration: const Duration(seconds: 4), + ), + ); + } + return; + } + } + ref + .read(settingsProvider.notifier) + .setDownloadDirectory(result); + } + }, + ), + Padding( + padding: const EdgeInsets.fromLTRB(24, 8, 24, 16), + child: Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: colorScheme.tertiaryContainer.withValues(alpha: 0.3), + borderRadius: BorderRadius.circular(12), + ), + child: Row( + children: [ + Icon(Icons.info_outline, size: 20, color: colorScheme.tertiary), + const SizedBox(width: 12), + Expanded( + child: Text( + context.l10n.setupIosEmptyFolderWarning, + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: colorScheme.onTertiaryContainer, + ), + ), + ), + ], + ), + ), + ), + const SizedBox(height: 8), + ], + ), + ), + ); + } + + void _showFormatEditor( + BuildContext context, + WidgetRef ref, + String current, { + void Function(String)? onSave, + String? title, + String? description, + }) { + final controller = TextEditingController(text: current); + final colorScheme = Theme.of(context).colorScheme; + + final basicTags = [ + '{artist}', '{title}', '{album}', '{track}', '{year}', '{date}', '{disc}', + ]; + final advancedTags = [ + '{track_raw}', '{track:02}', '{track:1}', + '{date:%Y}', '{date:%Y-%m-%d}', '{disc_raw}', '{disc:02}', + ]; + var showAdvancedTags = RegExp( + r'\{(?:track_raw|disc_raw|track:\d+|disc:\d+|date:[^}]+)\}', + caseSensitive: false, + ).hasMatch(current); + + void insertTag(String tag) { + final text = controller.text; + final selection = controller.selection; + final start = selection.start >= 0 ? selection.start : text.length; + final end = selection.end >= 0 ? selection.end : text.length; + String insertion = tag; + if (start > 0) { + final before = text.substring(0, start); + if (!before.trim().endsWith('-')) { + insertion = ' - $tag'; + } else if (before.trim().endsWith('-') && !before.endsWith(' ')) { + insertion = ' $tag'; + } + } + final newText = text.replaceRange(start, end, insertion); + controller.value = TextEditingValue( + text: newText, + selection: TextSelection.collapsed(offset: start + insertion.length), + ); + } + + showModalBottomSheet( + context: context, + useRootNavigator: true, + isScrollControlled: true, + backgroundColor: colorScheme.surface, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(28)), + ), + builder: (context) => StatefulBuilder( + builder: (context, setModalState) => Padding( + padding: EdgeInsets.only( + bottom: MediaQuery.of(context).viewInsets.bottom, + ), + child: SingleChildScrollView( + child: SafeArea( + child: Padding( + padding: const EdgeInsets.all(24), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Center( + child: Container( + width: 32, + height: 4, + margin: const EdgeInsets.only(bottom: 24), + decoration: BoxDecoration( + color: colorScheme.outlineVariant, + borderRadius: BorderRadius.circular(2), + ), + ), + ), + Text( + title ?? context.l10n.filenameFormat, + style: Theme.of(context).textTheme.headlineSmall + ?.copyWith(fontWeight: FontWeight.bold), + textAlign: TextAlign.center, + ), + const SizedBox(height: 8), + Text( + description ?? context.l10n.downloadFilenameDescription, + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + color: colorScheme.onSurfaceVariant, + ), + textAlign: TextAlign.center, + ), + const SizedBox(height: 24), + TextField( + controller: controller, + decoration: InputDecoration( + hintText: '{artist} - {title}', + filled: true, + fillColor: colorScheme.surfaceContainerHighest + .withValues(alpha: 0.3), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(16), + borderSide: BorderSide.none, + ), + ), + autofocus: true, + ), + const SizedBox(height: 24), + Text( + context.l10n.downloadFilenameInsertTag, + style: Theme.of(context).textTheme.titleSmall + ?.copyWith(fontWeight: FontWeight.bold), + ), + const SizedBox(height: 12), + Wrap( + spacing: 8, + runSpacing: 8, + children: basicTags.map((tag) { + return ActionChip( + label: Text(tag), + onPressed: () => insertTag(tag), + backgroundColor: colorScheme.surfaceContainerHighest + .withValues(alpha: 0.5), + side: BorderSide.none, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + labelStyle: TextStyle( + color: colorScheme.onSurface, + fontWeight: FontWeight.w500, + ), + ); + }).toList(), + ), + const SizedBox(height: 12), + SwitchListTile( + value: showAdvancedTags, + onChanged: (value) => + setModalState(() => showAdvancedTags = value), + contentPadding: EdgeInsets.zero, + title: Text(context.l10n.filenameShowAdvancedTags), + subtitle: Text( + context.l10n.filenameShowAdvancedTagsDescription, + ), + ), + if (showAdvancedTags) ...[ + const SizedBox(height: 8), + Wrap( + spacing: 8, + runSpacing: 8, + children: advancedTags.map((tag) { + return ActionChip( + label: Text(tag), + onPressed: () => insertTag(tag), + backgroundColor: colorScheme.surfaceContainerHighest + .withValues(alpha: 0.5), + side: BorderSide.none, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + labelStyle: TextStyle( + color: colorScheme.onSurface, + fontWeight: FontWeight.w500, + ), + ); + }).toList(), + ), + ], + const SizedBox(height: 32), + Row( + children: [ + Expanded( + child: TextButton( + onPressed: () => Navigator.pop(context), + style: TextButton.styleFrom( + padding: const EdgeInsets.symmetric(vertical: 16), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + ), + child: Text(context.l10n.dialogCancel), + ), + ), + const SizedBox(width: 12), + Expanded( + flex: 2, + child: FilledButton( + onPressed: () { + final save = onSave ?? + ref + .read(settingsProvider.notifier) + .setFilenameFormat; + save(controller.text); + Navigator.pop(context); + }, + style: FilledButton.styleFrom( + padding: const EdgeInsets.symmetric(vertical: 16), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + ), + child: Text(context.l10n.dialogSave), + ), + ), + ], + ), + const SizedBox(height: 8), + ], + ), + ), + ), + ), + ), + ), + ); + } + + void _showAlbumFolderStructurePicker( + BuildContext context, + WidgetRef ref, + String current, + ) { + showModalBottomSheet( + context: context, + useRootNavigator: true, + builder: (context) => SafeArea( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + for (final option in [ + ('artist_album', context.l10n.albumFolderArtistAlbum, + context.l10n.albumFolderArtistAlbumSubtitle, + Icons.folder_outlined), + ('artist_year_album', context.l10n.albumFolderArtistYearAlbum, + context.l10n.albumFolderArtistYearAlbumSubtitle, + Icons.calendar_today_outlined), + ('album_only', context.l10n.albumFolderAlbumOnly, + context.l10n.albumFolderAlbumOnlySubtitle, Icons.album_outlined), + ('year_album', context.l10n.albumFolderYearAlbum, + context.l10n.albumFolderYearAlbumSubtitle, + Icons.event_outlined), + ('artist_album_singles', context.l10n.albumFolderArtistAlbumSingles, + context.l10n.albumFolderArtistAlbumSinglesSubtitle, + Icons.person_outlined), + ('artist_album_flat', context.l10n.albumFolderArtistAlbumFlat, + context.l10n.albumFolderArtistAlbumFlatSubtitle, + Icons.person_outline_outlined), + ]) + ListTile( + leading: Icon(option.$4), + title: Text(option.$2), + subtitle: Text(option.$3), + trailing: current == option.$1 + ? const Icon(Icons.check) + : null, + onTap: () { + ref + .read(settingsProvider.notifier) + .setAlbumFolderStructure(option.$1); + Navigator.pop(context); + }, + ), + ], + ), + ), + ); + } + + void _showFolderOrganizationPicker( + BuildContext context, + WidgetRef ref, + String current, + ) { + final colorScheme = Theme.of(context).colorScheme; + showModalBottomSheet( + context: context, + useRootNavigator: true, + backgroundColor: colorScheme.surfaceContainerHigh, + isScrollControlled: true, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(28)), + ), + constraints: BoxConstraints( + maxHeight: MediaQuery.of(context).size.height * 0.7, + ), + builder: (context) => SafeArea( + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(24, 24, 24, 8), + child: Text( + context.l10n.downloadFolderOrganization, + style: Theme.of(context).textTheme.titleLarge + ?.copyWith(fontWeight: FontWeight.bold), + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(24, 0, 24, 16), + child: Text( + context.l10n.folderOrganizationDescription, + style: Theme.of(context).textTheme.bodyMedium + ?.copyWith(color: colorScheme.onSurfaceVariant), + ), + ), + for (final option in [ + ('none', context.l10n.folderOrganizationNone, + context.l10n.folderOrganizationNoneSubtitle, + 'SpotiFLAC/Track.flac'), + ('playlist', context.l10n.folderOrganizationByPlaylist, + context.l10n.folderOrganizationByPlaylistSubtitle, + 'SpotiFLAC/Playlist Name/Track.flac'), + ('artist', context.l10n.folderOrganizationByArtist, + context.l10n.folderOrganizationByArtistSubtitle, + 'SpotiFLAC/Artist Name/Track.flac'), + ('album', context.l10n.folderOrganizationByAlbum, + context.l10n.folderOrganizationByAlbumSubtitle, + 'SpotiFLAC/Album Name/Track.flac'), + ('artist_album', context.l10n.folderOrganizationByArtistAlbum, + context.l10n.folderOrganizationByArtistAlbumSubtitle, + 'SpotiFLAC/Artist/Album/Track.flac'), + ]) + _FolderOption( + title: option.$2, + subtitle: option.$3, + example: option.$4, + isSelected: current == option.$1, + onTap: () { + ref + .read(settingsProvider.notifier) + .setFolderOrganization(option.$1); + Navigator.pop(context); + }, + ), + ], + ), + ), + ), + ); + } +} + +class _FolderOption extends StatelessWidget { + final String title; + final String subtitle; + final String example; + final bool isSelected; + final VoidCallback onTap; + const _FolderOption({ + required this.title, + required this.subtitle, + required this.example, + required this.isSelected, + required this.onTap, + }); + + @override + Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return ListTile( + contentPadding: const EdgeInsets.symmetric(horizontal: 24, vertical: 4), + title: Text(title), + subtitle: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(subtitle), + const SizedBox(height: 4), + Text( + example, + style: TextStyle( + fontFamily: 'monospace', + fontSize: 11, + color: colorScheme.primary, + ), + ), + ], + ), + trailing: isSelected + ? Icon(Icons.check_circle, color: colorScheme.primary) + : Icon(Icons.circle_outlined, color: colorScheme.outline), + onTap: onTap, + ); + } +} diff --git a/lib/screens/settings/lyrics_settings_page.dart b/lib/screens/settings/lyrics_settings_page.dart new file mode 100644 index 00000000..862da242 --- /dev/null +++ b/lib/screens/settings/lyrics_settings_page.dart @@ -0,0 +1,373 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:spotiflac_android/l10n/l10n.dart'; +import 'package:spotiflac_android/providers/settings_provider.dart'; +import 'package:spotiflac_android/utils/app_bar_layout.dart'; +import 'package:spotiflac_android/screens/settings/lyrics_provider_priority_page.dart'; +import 'package:spotiflac_android/widgets/settings_group.dart'; + +class LyricsSettingsPage extends ConsumerWidget { + const LyricsSettingsPage({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final settings = ref.watch(settingsProvider); + final colorScheme = Theme.of(context).colorScheme; + final topPadding = normalizedHeaderTopPadding(context); + + return PopScope( + canPop: true, + child: Scaffold( + body: CustomScrollView( + slivers: [ + SliverAppBar( + expandedHeight: 120 + topPadding, + collapsedHeight: kToolbarHeight, + floating: false, + pinned: true, + backgroundColor: colorScheme.surface, + surfaceTintColor: Colors.transparent, + leading: IconButton( + tooltip: MaterialLocalizations.of(context).backButtonTooltip, + icon: const Icon(Icons.arrow_back), + onPressed: () => Navigator.pop(context), + ), + flexibleSpace: LayoutBuilder( + builder: (context, constraints) { + final maxHeight = 120 + topPadding; + final minHeight = kToolbarHeight + topPadding; + final expandRatio = + ((constraints.maxHeight - minHeight) / + (maxHeight - minHeight)) + .clamp(0.0, 1.0); + final leftPadding = 56 - (32 * expandRatio); + return FlexibleSpaceBar( + expandedTitleScale: 1.0, + titlePadding: EdgeInsets.only( + left: leftPadding, + bottom: 16, + ), + title: Text( + context.l10n.settingsLyrics, + style: TextStyle( + fontSize: 20 + (8 * expandRatio), + fontWeight: FontWeight.bold, + color: colorScheme.onSurface, + ), + ), + ); + }, + ), + ), + + // ── Lyrics Embedding ─────────────────────────────────────── + SliverToBoxAdapter( + child: SettingsSectionHeader(title: context.l10n.sectionLyrics), + ), + SliverToBoxAdapter( + child: SettingsGroup( + children: [ + SettingsSwitchItem( + icon: Icons.subtitles_outlined, + title: context.l10n.optionsEmbedLyrics, + subtitle: settings.embedMetadata + ? context.l10n.optionsEmbedLyricsSubtitle + : context.l10n.downloadEmbedLyricsDisabled, + value: settings.embedLyrics, + enabled: settings.embedMetadata, + onChanged: (value) => ref + .read(settingsProvider.notifier) + .setEmbedLyrics(value), + showDivider: + settings.embedMetadata && settings.embedLyrics, + ), + if (settings.embedMetadata && settings.embedLyrics) ...[ + SettingsItem( + icon: Icons.lyrics_outlined, + title: context.l10n.lyricsMode, + subtitle: _getLyricsModeLabel( + context, + settings.lyricsMode, + ), + onTap: () => + _showLyricsModePicker(context, ref, settings.lyricsMode), + ), + SettingsItem( + icon: Icons.source_outlined, + title: context.l10n.lyricsProvidersTitle, + subtitle: _getLyricsProvidersSubtitle( + context, + settings.lyricsProviders, + ), + onTap: () => Navigator.push( + context, + MaterialPageRoute( + builder: (_) => const LyricsProviderPriorityPage(), + ), + ), + showDivider: false, + ), + ], + ], + ), + ), + + // ── Provider Options ─────────────────────────────────────── + if (settings.embedMetadata && settings.embedLyrics) ...[ + SliverToBoxAdapter( + child: SettingsSectionHeader( + title: context.l10n.sectionLyricsProviderOptions, + ), + ), + SliverToBoxAdapter( + child: SettingsGroup( + children: [ + SettingsSwitchItem( + icon: Icons.translate_outlined, + title: context.l10n.downloadNeteaseIncludeTranslation, + subtitle: settings.lyricsIncludeTranslationNetease + ? context.l10n.downloadNeteaseIncludeTranslationEnabled + : context.l10n.downloadNeteaseIncludeTranslationDisabled, + value: settings.lyricsIncludeTranslationNetease, + onChanged: (value) => ref + .read(settingsProvider.notifier) + .setLyricsIncludeTranslationNetease(value), + ), + SettingsSwitchItem( + icon: Icons.text_fields_outlined, + title: context.l10n.downloadNeteaseIncludeRomanization, + subtitle: settings.lyricsIncludeRomanizationNetease + ? context + .l10n + .downloadNeteaseIncludeRomanizationEnabled + : context + .l10n + .downloadNeteaseIncludeRomanizationDisabled, + value: settings.lyricsIncludeRomanizationNetease, + onChanged: (value) => ref + .read(settingsProvider.notifier) + .setLyricsIncludeRomanizationNetease(value), + ), + SettingsSwitchItem( + icon: Icons.record_voice_over_outlined, + title: context.l10n.downloadAppleQqMultiPerson, + subtitle: settings.lyricsMultiPersonWordByWord + ? context.l10n.downloadAppleQqMultiPersonEnabled + : context.l10n.downloadAppleQqMultiPersonDisabled, + value: settings.lyricsMultiPersonWordByWord, + onChanged: (value) => ref + .read(settingsProvider.notifier) + .setLyricsMultiPersonWordByWord(value), + ), + SettingsItem( + icon: Icons.language_outlined, + title: context.l10n.downloadMusixmatchLanguage, + subtitle: settings.musixmatchLanguage.isEmpty + ? context.l10n.downloadMusixmatchLanguageAuto + : settings.musixmatchLanguage.toUpperCase(), + onTap: () => _showMusixmatchLanguagePicker( + context, + ref, + settings.musixmatchLanguage, + ), + showDivider: false, + ), + ], + ), + ), + ], + + const SliverToBoxAdapter(child: SizedBox(height: 32)), + ], + ), + ), + ); + } + + String _getLyricsModeLabel(BuildContext context, String mode) { + switch (mode) { + case 'external': + return context.l10n.lyricsModeExternal; + case 'both': + return context.l10n.lyricsModeBoth; + default: + return context.l10n.lyricsModeEmbed; + } + } + + static const _providerDisplayNames = { + 'lrclib': 'LRCLIB', + 'netease': 'Netease', + 'musixmatch': 'Musixmatch', + 'apple_music': 'Apple Music', + 'qqmusic': 'QQ Music', + }; + + String _getLyricsProvidersSubtitle( + BuildContext context, + List providers, + ) { + if (providers.isEmpty) return context.l10n.downloadProvidersNoneEnabled; + return providers + .map((p) => _providerDisplayNames[p] ?? p) + .join(' > '); + } + + void _showLyricsModePicker( + BuildContext context, + WidgetRef ref, + String current, + ) { + final colorScheme = Theme.of(context).colorScheme; + showModalBottomSheet( + context: context, + useRootNavigator: true, + backgroundColor: colorScheme.surfaceContainerHigh, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(28)), + ), + builder: (context) => SafeArea( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(24, 24, 24, 8), + child: Text( + context.l10n.lyricsMode, + style: Theme.of(context).textTheme.titleLarge + ?.copyWith(fontWeight: FontWeight.bold), + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(24, 0, 24, 16), + child: Text( + context.l10n.lyricsModeDescription, + style: Theme.of(context).textTheme.bodyMedium + ?.copyWith(color: colorScheme.onSurfaceVariant), + ), + ), + ListTile( + leading: const Icon(Icons.audiotrack), + title: Text(context.l10n.lyricsModeEmbed), + subtitle: Text(context.l10n.lyricsModeEmbedSubtitle), + trailing: current == 'embed' ? const Icon(Icons.check) : null, + onTap: () { + ref.read(settingsProvider.notifier).setLyricsMode('embed'); + Navigator.pop(context); + }, + ), + ListTile( + leading: const Icon(Icons.insert_drive_file_outlined), + title: Text(context.l10n.lyricsModeExternal), + subtitle: Text(context.l10n.lyricsModeExternalSubtitle), + trailing: current == 'external' ? const Icon(Icons.check) : null, + onTap: () { + ref.read(settingsProvider.notifier).setLyricsMode('external'); + Navigator.pop(context); + }, + ), + ListTile( + leading: const Icon(Icons.library_music_outlined), + title: Text(context.l10n.lyricsModeBoth), + subtitle: Text(context.l10n.lyricsModeBothSubtitle), + trailing: current == 'both' ? const Icon(Icons.check) : null, + onTap: () { + ref.read(settingsProvider.notifier).setLyricsMode('both'); + Navigator.pop(context); + }, + ), + const SizedBox(height: 16), + ], + ), + ), + ); + } + + void _showMusixmatchLanguagePicker( + BuildContext context, + WidgetRef ref, + String currentLanguage, + ) { + final colorScheme = Theme.of(context).colorScheme; + final controller = TextEditingController(text: currentLanguage); + + showModalBottomSheet( + context: context, + useRootNavigator: true, + backgroundColor: colorScheme.surfaceContainerHigh, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(28)), + ), + isScrollControlled: true, + builder: (context) => Padding( + padding: EdgeInsets.only( + left: 24, + right: 24, + top: 24, + bottom: 24 + MediaQuery.of(context).viewInsets.bottom, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + context.l10n.downloadMusixmatchLanguage, + style: Theme.of(context).textTheme.titleLarge + ?.copyWith(fontWeight: FontWeight.bold), + ), + const SizedBox(height: 8), + Text( + context.l10n.downloadMusixmatchLanguageDesc, + style: Theme.of(context).textTheme.bodyMedium + ?.copyWith(color: colorScheme.onSurfaceVariant), + ), + const SizedBox(height: 16), + TextField( + controller: controller, + textInputAction: TextInputAction.done, + decoration: InputDecoration( + labelText: context.l10n.downloadMusixmatchLanguageCode, + hintText: context.l10n.downloadMusixmatchLanguageHint, + ), + ), + const SizedBox(height: 16), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + TextButton( + onPressed: () => Navigator.pop(context), + child: Text(context.l10n.dialogCancel), + ), + const SizedBox(width: 8), + TextButton( + onPressed: () { + ref + .read(settingsProvider.notifier) + .setMusixmatchLanguage(''); + Navigator.pop(context); + }, + child: Text(context.l10n.downloadMusixmatchAuto), + ), + const SizedBox(width: 8), + FilledButton( + onPressed: () { + final normalized = controller.text + .trim() + .toLowerCase() + .replaceAll(RegExp(r'[^a-z0-9\-_]'), ''); + ref + .read(settingsProvider.notifier) + .setMusixmatchLanguage(normalized); + Navigator.pop(context); + }, + child: Text(context.l10n.dialogSave), + ), + ], + ), + ], + ), + ), + ); + } +} diff --git a/lib/screens/settings/metadata_settings_page.dart b/lib/screens/settings/metadata_settings_page.dart new file mode 100644 index 00000000..83f1afb1 --- /dev/null +++ b/lib/screens/settings/metadata_settings_page.dart @@ -0,0 +1,253 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:spotiflac_android/l10n/l10n.dart'; +import 'package:spotiflac_android/providers/settings_provider.dart'; +import 'package:spotiflac_android/utils/app_bar_layout.dart'; +import 'package:spotiflac_android/utils/artist_utils.dart'; +import 'package:spotiflac_android/screens/settings/metadata_provider_priority_page.dart'; +import 'package:spotiflac_android/widgets/settings_group.dart'; + +class MetadataSettingsPage extends ConsumerWidget { + const MetadataSettingsPage({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final settings = ref.watch(settingsProvider); + final colorScheme = Theme.of(context).colorScheme; + final topPadding = normalizedHeaderTopPadding(context); + + return PopScope( + canPop: true, + child: Scaffold( + body: CustomScrollView( + slivers: [ + SliverAppBar( + expandedHeight: 120 + topPadding, + collapsedHeight: kToolbarHeight, + floating: false, + pinned: true, + backgroundColor: colorScheme.surface, + surfaceTintColor: Colors.transparent, + leading: IconButton( + tooltip: MaterialLocalizations.of(context).backButtonTooltip, + icon: const Icon(Icons.arrow_back), + onPressed: () => Navigator.pop(context), + ), + flexibleSpace: LayoutBuilder( + builder: (context, constraints) { + final maxHeight = 120 + topPadding; + final minHeight = kToolbarHeight + topPadding; + final expandRatio = + ((constraints.maxHeight - minHeight) / + (maxHeight - minHeight)) + .clamp(0.0, 1.0); + final leftPadding = 56 - (32 * expandRatio); + return FlexibleSpaceBar( + expandedTitleScale: 1.0, + titlePadding: EdgeInsets.only( + left: leftPadding, + bottom: 16, + ), + title: Text( + context.l10n.settingsMetadata, + style: TextStyle( + fontSize: 20 + (8 * expandRatio), + fontWeight: FontWeight.bold, + color: colorScheme.onSurface, + ), + ), + ); + }, + ), + ), + + // ── Embedding ────────────────────────────────────────────── + SliverToBoxAdapter( + child: SettingsSectionHeader(title: context.l10n.sectionDownload), + ), + SliverToBoxAdapter( + child: SettingsGroup( + children: [ + SettingsSwitchItem( + icon: Icons.sell_outlined, + title: 'Embed Metadata', + subtitle: settings.embedMetadata + ? 'Write metadata, cover art, and lyrics to files' + : 'Disabled (advanced): skip all metadata embedding', + value: settings.embedMetadata, + onChanged: (v) => + ref.read(settingsProvider.notifier).setEmbedMetadata(v), + showDivider: settings.embedMetadata, + ), + if (settings.embedMetadata) ...[ + SettingsItem( + icon: Icons.people_alt_outlined, + title: context.l10n.optionsArtistTagMode, + subtitle: _getArtistTagModeLabel( + context, + settings.artistTagMode, + ), + onTap: () => + _showArtistTagModePicker(context, ref, settings.artistTagMode), + ), + SettingsSwitchItem( + icon: Icons.image, + title: context.l10n.optionsMaxQualityCover, + subtitle: context.l10n.optionsMaxQualityCoverSubtitle, + value: settings.maxQualityCover, + onChanged: (v) => ref + .read(settingsProvider.notifier) + .setMaxQualityCover(v), + ), + SettingsSwitchItem( + icon: Icons.graphic_eq, + title: context.l10n.optionsReplayGain, + subtitle: settings.embedReplayGain + ? context.l10n.optionsReplayGainSubtitleOn + : context.l10n.optionsReplayGainSubtitleOff, + value: settings.embedReplayGain, + onChanged: (v) => ref + .read(settingsProvider.notifier) + .setEmbedReplayGain(v), + showDivider: false, + ), + ], + ], + ), + ), + + // ── Providers ───────────────────────────────────────────── + SliverToBoxAdapter( + child: SettingsSectionHeader( + title: context.l10n.sectionMetadataProviders, + ), + ), + SliverToBoxAdapter( + child: SettingsGroup( + children: [ + SettingsItem( + icon: Icons.source_outlined, + title: context.l10n.metadataProvidersTitle, + subtitle: context.l10n.metadataProvidersSubtitle, + onTap: () => Navigator.push( + context, + MaterialPageRoute( + builder: (_) => const MetadataProviderPriorityPage(), + ), + ), + showDivider: false, + ), + ], + ), + ), + + // ── Deduplication ────────────────────────────────────────── + SliverToBoxAdapter( + child: SettingsSectionHeader( + title: context.l10n.sectionDuplicates, + ), + ), + SliverToBoxAdapter( + child: SettingsGroup( + children: [ + SettingsSwitchItem( + icon: Icons.filter_list_outlined, + title: context.l10n.downloadDeduplication, + subtitle: settings.deduplicateDownloads + ? context.l10n.downloadDeduplicationEnabled + : context.l10n.downloadDeduplicationDisabled, + value: settings.deduplicateDownloads, + onChanged: (value) => ref + .read(settingsProvider.notifier) + .setDeduplicateDownloads(value), + showDivider: false, + ), + ], + ), + ), + + const SliverToBoxAdapter(child: SizedBox(height: 32)), + ], + ), + ), + ); + } + + String _getArtistTagModeLabel(BuildContext context, String mode) { + switch (mode) { + case artistTagModeSplitVorbis: + return context.l10n.optionsArtistTagModeSplitVorbis; + default: + return context.l10n.optionsArtistTagModeJoined; + } + } + + void _showArtistTagModePicker( + BuildContext context, + WidgetRef ref, + String currentMode, + ) { + final colorScheme = Theme.of(context).colorScheme; + showModalBottomSheet( + context: context, + useRootNavigator: true, + backgroundColor: colorScheme.surfaceContainerHigh, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(28)), + ), + builder: (context) => SafeArea( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(24, 24, 24, 8), + child: Text( + context.l10n.optionsArtistTagMode, + style: Theme.of(context).textTheme.titleLarge + ?.copyWith(fontWeight: FontWeight.bold), + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(24, 0, 24, 16), + child: Text( + context.l10n.optionsArtistTagModeDescription, + style: Theme.of(context).textTheme.bodyMedium + ?.copyWith(color: colorScheme.onSurfaceVariant), + ), + ), + ListTile( + leading: const Icon(Icons.segment_outlined), + title: Text(context.l10n.optionsArtistTagModeJoined), + subtitle: Text(context.l10n.optionsArtistTagModeJoinedSubtitle), + trailing: currentMode == artistTagModeJoined + ? const Icon(Icons.check) + : null, + onTap: () { + ref + .read(settingsProvider.notifier) + .setArtistTagMode(artistTagModeJoined); + Navigator.pop(context); + }, + ), + ListTile( + leading: const Icon(Icons.library_music_outlined), + title: Text(context.l10n.optionsArtistTagModeSplitVorbis), + subtitle: Text(context.l10n.optionsArtistTagModeSplitVorbisSubtitle), + trailing: currentMode == artistTagModeSplitVorbis + ? const Icon(Icons.check) + : null, + onTap: () { + ref + .read(settingsProvider.notifier) + .setArtistTagMode(artistTagModeSplitVorbis); + Navigator.pop(context); + }, + ), + const SizedBox(height: 16), + ], + ), + ), + ); + } +} diff --git a/lib/screens/settings/options_settings_page.dart b/lib/screens/settings/options_settings_page.dart deleted file mode 100644 index 147394aa..00000000 --- a/lib/screens/settings/options_settings_page.dart +++ /dev/null @@ -1,1012 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:spotiflac_android/l10n/l10n.dart'; -import 'package:spotiflac_android/providers/download_queue_provider.dart'; -import 'package:spotiflac_android/providers/extension_provider.dart'; -import 'package:spotiflac_android/providers/settings_provider.dart'; -import 'package:spotiflac_android/utils/app_bar_layout.dart'; -import 'package:spotiflac_android/utils/artist_utils.dart'; -import 'package:spotiflac_android/widgets/settings_group.dart'; - -class OptionsSettingsPage extends ConsumerWidget { - const OptionsSettingsPage({super.key}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final settings = ref.watch(settingsProvider); - final extensionState = ref.watch(extensionProvider); - final hasExtensions = extensionState.extensions.isNotEmpty; - final colorScheme = Theme.of(context).colorScheme; - final topPadding = normalizedHeaderTopPadding(context); - - return PopScope( - canPop: true, - child: Scaffold( - body: CustomScrollView( - slivers: [ - SliverAppBar( - expandedHeight: 120 + topPadding, - collapsedHeight: kToolbarHeight, - floating: false, - pinned: true, - backgroundColor: colorScheme.surface, - surfaceTintColor: Colors.transparent, - leading: IconButton( - tooltip: MaterialLocalizations.of(context).backButtonTooltip, - icon: const Icon(Icons.arrow_back), - onPressed: () => Navigator.pop(context), - ), - flexibleSpace: LayoutBuilder( - builder: (context, constraints) { - final maxHeight = 120 + topPadding; - final minHeight = kToolbarHeight + topPadding; - final expandRatio = - ((constraints.maxHeight - minHeight) / - (maxHeight - minHeight)) - .clamp(0.0, 1.0); - final leftPadding = 56 - (32 * expandRatio); - return FlexibleSpaceBar( - expandedTitleScale: 1.0, - titlePadding: EdgeInsets.only( - left: leftPadding, - bottom: 16, - ), - title: Text( - context.l10n.optionsTitle, - style: TextStyle( - fontSize: 20 + (8 * expandRatio), - fontWeight: FontWeight.bold, - color: colorScheme.onSurface, - ), - ), - ); - }, - ), - ), - - SliverToBoxAdapter( - child: SettingsSectionHeader( - title: context.l10n.sectionSearchSource, - ), - ), - SliverToBoxAdapter( - child: SettingsGroup( - children: const [ - _MetadataSourceSelector(), - _DefaultSearchTabSelector(), - ], - ), - ), - - SliverToBoxAdapter( - child: SettingsSectionHeader(title: context.l10n.sectionDownload), - ), - SliverToBoxAdapter( - child: SettingsGroup( - children: [ - SettingsSwitchItem( - icon: Icons.sync, - title: context.l10n.optionsAutoFallback, - subtitle: context.l10n.optionsAutoFallbackSubtitle, - value: settings.autoFallback, - onChanged: (v) => - ref.read(settingsProvider.notifier).setAutoFallback(v), - ), - if (hasExtensions) - SettingsSwitchItem( - icon: Icons.extension, - title: context.l10n.optionsUseExtensionProviders, - subtitle: settings.useExtensionProviders - ? context.l10n.optionsUseExtensionProvidersOn - : context.l10n.optionsUseExtensionProvidersOff, - value: settings.useExtensionProviders, - onChanged: (v) => ref - .read(settingsProvider.notifier) - .setUseExtensionProviders(v), - ), - SettingsSwitchItem( - icon: Icons.sell_outlined, - title: 'Embed Metadata', - subtitle: settings.embedMetadata - ? 'Write metadata, cover art, and embedded lyrics to files' - : 'Disabled (advanced): skip all metadata embedding', - value: settings.embedMetadata, - onChanged: (v) => - ref.read(settingsProvider.notifier).setEmbedMetadata(v), - showDivider: settings.embedMetadata, - ), - if (settings.embedMetadata) - SettingsItem( - icon: Icons.people_alt_outlined, - title: context.l10n.optionsArtistTagMode, - subtitle: _getArtistTagModeLabel( - context, - settings.artistTagMode, - ), - onTap: () => _showArtistTagModePicker( - context, - ref, - settings.artistTagMode, - ), - ), - SettingsSwitchItem( - icon: Icons.image, - title: context.l10n.optionsMaxQualityCover, - subtitle: settings.embedMetadata - ? context.l10n.optionsMaxQualityCoverSubtitle - : 'Disabled when metadata embedding is off', - value: settings.maxQualityCover, - enabled: settings.embedMetadata, - onChanged: (v) => ref - .read(settingsProvider.notifier) - .setMaxQualityCover(v), - ), - SettingsSwitchItem( - icon: Icons.graphic_eq, - title: context.l10n.optionsReplayGain, - subtitle: settings.embedReplayGain - ? context.l10n.optionsReplayGainSubtitleOn - : context.l10n.optionsReplayGainSubtitleOff, - value: settings.embedReplayGain, - enabled: settings.embedMetadata, - onChanged: (v) => ref - .read(settingsProvider.notifier) - .setEmbedReplayGain(v), - showDivider: false, - ), - ], - ), - ), - - SliverToBoxAdapter( - child: SettingsSectionHeader( - title: context.l10n.sectionPerformance, - ), - ), - SliverToBoxAdapter( - child: SettingsGroup( - children: [ - _ConcurrentDownloadsItem( - currentValue: settings.concurrentDownloads, - onChanged: (v) => ref - .read(settingsProvider.notifier) - .setConcurrentDownloads(v), - ), - ], - ), - ), - - SliverToBoxAdapter( - child: SettingsSectionHeader(title: context.l10n.sectionApp), - ), - SliverToBoxAdapter( - child: SettingsGroup( - children: [ - SettingsSwitchItem( - icon: Icons.extension, - title: context.l10n.optionsExtensionStore, - subtitle: context.l10n.optionsExtensionStoreSubtitle, - value: settings.showExtensionStore, - onChanged: (v) => ref - .read(settingsProvider.notifier) - .setShowExtensionStore(v), - ), - SettingsSwitchItem( - icon: Icons.system_update, - title: context.l10n.optionsCheckUpdates, - subtitle: context.l10n.optionsCheckUpdatesSubtitle, - value: settings.checkForUpdates, - onChanged: (v) => ref - .read(settingsProvider.notifier) - .setCheckForUpdates(v), - ), - _UpdateChannelSelector( - currentChannel: settings.updateChannel, - onChanged: (v) => - ref.read(settingsProvider.notifier).setUpdateChannel(v), - ), - ], - ), - ), - - SliverToBoxAdapter( - child: SettingsSectionHeader(title: context.l10n.sectionData), - ), - SliverToBoxAdapter( - child: SettingsGroup( - children: [ - SettingsItem( - icon: Icons.cleaning_services_outlined, - title: context.l10n.cleanupOrphanedDownloads, - subtitle: context.l10n.cleanupOrphanedDownloadsSubtitle, - onTap: () => _cleanupOrphanedDownloads(context, ref), - ), - SettingsItem( - icon: Icons.delete_forever, - title: context.l10n.optionsClearHistory, - subtitle: context.l10n.optionsClearHistorySubtitle, - onTap: () => - _showClearHistoryDialog(context, ref, colorScheme), - showDivider: false, - ), - ], - ), - ), - - SliverToBoxAdapter( - child: SettingsSectionHeader(title: context.l10n.sectionDebug), - ), - SliverToBoxAdapter( - child: SettingsGroup( - children: [ - SettingsSwitchItem( - icon: Icons.bug_report, - title: context.l10n.optionsDetailedLogging, - subtitle: settings.enableLogging - ? context.l10n.optionsDetailedLoggingOn - : context.l10n.optionsDetailedLoggingOff, - value: settings.enableLogging, - onChanged: (v) => - ref.read(settingsProvider.notifier).setEnableLogging(v), - showDivider: false, - ), - ], - ), - ), - - const SliverToBoxAdapter(child: SizedBox(height: 32)), - ], - ), - ), - ); - } - - String _getArtistTagModeLabel(BuildContext context, String mode) { - switch (mode) { - case artistTagModeSplitVorbis: - return context.l10n.optionsArtistTagModeSplitVorbis; - default: - return context.l10n.optionsArtistTagModeJoined; - } - } - - void _showArtistTagModePicker( - BuildContext context, - WidgetRef ref, - String currentMode, - ) { - final colorScheme = Theme.of(context).colorScheme; - showModalBottomSheet( - context: context, - useRootNavigator: true, - backgroundColor: colorScheme.surfaceContainerHigh, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical(top: Radius.circular(28)), - ), - builder: (context) => SafeArea( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.fromLTRB(24, 24, 24, 8), - child: Text( - context.l10n.optionsArtistTagMode, - style: Theme.of( - context, - ).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold), - ), - ), - Padding( - padding: const EdgeInsets.fromLTRB(24, 0, 24, 16), - child: Text( - context.l10n.optionsArtistTagModeDescription, - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: colorScheme.onSurfaceVariant, - ), - ), - ), - ListTile( - leading: const Icon(Icons.segment_outlined), - title: Text(context.l10n.optionsArtistTagModeJoined), - subtitle: Text(context.l10n.optionsArtistTagModeJoinedSubtitle), - trailing: currentMode == artistTagModeJoined - ? const Icon(Icons.check) - : null, - onTap: () { - ref - .read(settingsProvider.notifier) - .setArtistTagMode(artistTagModeJoined); - Navigator.pop(context); - }, - ), - ListTile( - leading: const Icon(Icons.library_music_outlined), - title: Text(context.l10n.optionsArtistTagModeSplitVorbis), - subtitle: Text( - context.l10n.optionsArtistTagModeSplitVorbisSubtitle, - ), - trailing: currentMode == artistTagModeSplitVorbis - ? const Icon(Icons.check) - : null, - onTap: () { - ref - .read(settingsProvider.notifier) - .setArtistTagMode(artistTagModeSplitVorbis); - Navigator.pop(context); - }, - ), - const SizedBox(height: 16), - ], - ), - ), - ); - } - - void _showClearHistoryDialog( - BuildContext context, - WidgetRef ref, - ColorScheme colorScheme, - ) { - showDialog( - context: context, - builder: (context) => AlertDialog( - title: Text(context.l10n.dialogClearHistoryTitle), - content: Text(context.l10n.dialogClearHistoryMessage), - actions: [ - TextButton( - onPressed: () => Navigator.pop(context), - child: Text(context.l10n.dialogCancel), - ), - TextButton( - onPressed: () { - ref.read(downloadHistoryProvider.notifier).clearHistory(); - Navigator.pop(context); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(context.l10n.snackbarHistoryCleared)), - ); - }, - child: Text( - context.l10n.dialogClear, - style: TextStyle(color: colorScheme.error), - ), - ), - ], - ), - ); - } - - Future _cleanupOrphanedDownloads( - BuildContext context, - WidgetRef ref, - ) async { - showDialog( - context: context, - barrierDismissible: false, - builder: (context) => AlertDialog( - content: Row( - children: [ - const CircularProgressIndicator(), - const SizedBox(width: 16), - Text(context.l10n.cleanupOrphanedDownloads), - ], - ), - ), - ); - - try { - final removed = await ref - .read(downloadHistoryProvider.notifier) - .cleanupOrphanedDownloads(); - - if (context.mounted) { - Navigator.pop(context); // Close loading dialog - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - removed > 0 - ? context.l10n.cleanupOrphanedDownloadsResult(removed) - : context.l10n.cleanupOrphanedDownloadsNone, - ), - ), - ); - } - } catch (e) { - if (context.mounted) { - Navigator.pop(context); // Close loading dialog - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(context.l10n.snackbarError(e.toString()))), - ); - } - } - } -} - -class _ConcurrentDownloadsItem extends StatelessWidget { - final int currentValue; - final ValueChanged onChanged; - const _ConcurrentDownloadsItem({ - required this.currentValue, - required this.onChanged, - }); - - @override - Widget build(BuildContext context) { - final colorScheme = Theme.of(context).colorScheme; - return Padding( - padding: const EdgeInsets.all(20), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Icon( - Icons.download_for_offline, - color: colorScheme.onSurfaceVariant, - size: 24, - ), - const SizedBox(width: 16), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - context.l10n.optionsConcurrentDownloads, - style: Theme.of(context).textTheme.bodyLarge, - ), - const SizedBox(height: 2), - Text( - currentValue == 1 - ? context.l10n.optionsConcurrentSequential - : context.l10n.optionsConcurrentParallel( - currentValue, - ), - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: colorScheme.onSurfaceVariant, - ), - ), - ], - ), - ), - ], - ), - const SizedBox(height: 16), - Row( - children: [ - _ConcurrentChip( - label: '1', - isSelected: currentValue == 1, - onTap: () => onChanged(1), - ), - const SizedBox(width: 8), - _ConcurrentChip( - label: '2', - isSelected: currentValue == 2, - onTap: () => onChanged(2), - ), - const SizedBox(width: 8), - _ConcurrentChip( - label: '3', - isSelected: currentValue == 3, - onTap: () => onChanged(3), - ), - const SizedBox(width: 8), - _ConcurrentChip( - label: '4', - isSelected: currentValue == 4, - onTap: () => onChanged(4), - ), - const SizedBox(width: 8), - _ConcurrentChip( - label: '5', - isSelected: currentValue == 5, - onTap: () => onChanged(5), - ), - ], - ), - const SizedBox(height: 12), - Row( - children: [ - Icon( - Icons.warning_amber_rounded, - size: 16, - color: colorScheme.error, - ), - const SizedBox(width: 8), - Expanded( - child: Text( - context.l10n.optionsConcurrentWarning, - style: Theme.of( - context, - ).textTheme.bodySmall?.copyWith(color: colorScheme.error), - ), - ), - ], - ), - ], - ), - ); - } -} - -class _ConcurrentChip extends StatelessWidget { - final String label; - final bool isSelected; - final VoidCallback onTap; - const _ConcurrentChip({ - required this.label, - required this.isSelected, - required this.onTap, - }); - - @override - Widget build(BuildContext context) { - final colorScheme = Theme.of(context).colorScheme; - final isDark = Theme.of(context).brightness == Brightness.dark; - - final unselectedColor = isDark - ? Color.alphaBlend( - Colors.white.withValues(alpha: 0.05), - colorScheme.surface, - ) - : colorScheme.surfaceContainerHigh; - - return Expanded( - child: Material( - color: isSelected ? colorScheme.primaryContainer : unselectedColor, - borderRadius: BorderRadius.circular(12), - child: InkWell( - onTap: onTap, - borderRadius: BorderRadius.circular(12), - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 12), - child: Center( - child: Text( - label, - style: TextStyle( - fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal, - color: isSelected - ? colorScheme.onPrimaryContainer - : colorScheme.onSurfaceVariant, - ), - ), - ), - ), - ), - ), - ); - } -} - -class _UpdateChannelSelector extends StatelessWidget { - final String currentChannel; - final ValueChanged onChanged; - const _UpdateChannelSelector({ - required this.currentChannel, - required this.onChanged, - }); - - @override - Widget build(BuildContext context) { - final colorScheme = Theme.of(context).colorScheme; - return Padding( - padding: const EdgeInsets.all(20), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Icon( - Icons.new_releases, - color: colorScheme.onSurfaceVariant, - size: 24, - ), - const SizedBox(width: 16), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - context.l10n.optionsUpdateChannel, - style: Theme.of(context).textTheme.bodyLarge, - ), - const SizedBox(height: 2), - Text( - currentChannel == 'preview' - ? context.l10n.optionsUpdateChannelPreview - : context.l10n.optionsUpdateChannelStable, - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: colorScheme.onSurfaceVariant, - ), - ), - ], - ), - ), - ], - ), - const SizedBox(height: 16), - Row( - children: [ - _ChannelChip( - label: context.l10n.channelStable, - isSelected: currentChannel == 'stable', - onTap: () => onChanged('stable'), - ), - const SizedBox(width: 8), - _ChannelChip( - label: context.l10n.channelPreview, - isSelected: currentChannel == 'preview', - onTap: () => onChanged('preview'), - ), - ], - ), - const SizedBox(height: 12), - Row( - children: [ - Icon( - Icons.info_outline, - size: 16, - color: colorScheme.onSurfaceVariant, - ), - const SizedBox(width: 8), - Expanded( - child: Text( - context.l10n.optionsUpdateChannelWarning, - style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: colorScheme.onSurfaceVariant, - ), - ), - ), - ], - ), - ], - ), - ); - } -} - -class _ChannelChip extends StatelessWidget { - final String label; - final bool isSelected; - final VoidCallback onTap; - const _ChannelChip({ - required this.label, - required this.isSelected, - required this.onTap, - }); - - @override - Widget build(BuildContext context) { - final colorScheme = Theme.of(context).colorScheme; - final isDark = Theme.of(context).brightness == Brightness.dark; - - final unselectedColor = isDark - ? Color.alphaBlend( - Colors.white.withValues(alpha: 0.05), - colorScheme.surface, - ) - : colorScheme.surfaceContainerHigh; - - return Expanded( - child: Material( - color: isSelected ? colorScheme.primaryContainer : unselectedColor, - borderRadius: BorderRadius.circular(12), - child: InkWell( - onTap: onTap, - borderRadius: BorderRadius.circular(12), - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 12), - child: Center( - child: Text( - label, - style: TextStyle( - fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal, - color: isSelected - ? colorScheme.onPrimaryContainer - : colorScheme.onSurfaceVariant, - ), - ), - ), - ), - ), - ), - ); - } -} - -class _MetadataSourceSelector extends ConsumerWidget { - const _MetadataSourceSelector(); - - static const _builtInProviders = {'tidal': 'Tidal', 'qobuz': 'Qobuz'}; - - Extension? _defaultSearchExtension(List extensions) { - return extensions - .where( - (ext) => - ext.enabled && - ext.hasCustomSearch && - ext.searchBehavior?.primary == true, - ) - .firstOrNull ?? - extensions - .where((ext) => ext.enabled && ext.hasCustomSearch) - .firstOrNull; - } - - @override - Widget build(BuildContext context, WidgetRef ref) { - final colorScheme = Theme.of(context).colorScheme; - final settings = ref.watch(settingsProvider); - final extState = ref.watch(extensionProvider); - - final rawSearchProvider = settings.searchProvider?.trim() ?? ''; - final isValidBuiltIn = _builtInProviders.containsKey(rawSearchProvider); - final primarySearchExtension = _defaultSearchExtension(extState.extensions); - final defaultProviderTarget = - primarySearchExtension?.displayName ?? 'Tidal'; - final defaultProviderLabel = - '${context.l10n.extensionsHomeFeedAuto} ($defaultProviderTarget)'; - final searchProvider = - isValidBuiltIn || - extState.extensions.any( - (e) => - e.enabled && e.hasCustomSearch && e.id == rawSearchProvider, - ) - ? rawSearchProvider - : ''; - final isBuiltIn = _builtInProviders.containsKey(searchProvider); - - Extension? activeExtension; - if (searchProvider.isNotEmpty && !isBuiltIn) { - activeExtension = extState.extensions - .where((e) => e.id == searchProvider && e.enabled) - .firstOrNull; - } - final hasNonDefaultProvider = isBuiltIn || activeExtension != null; - - String subtitle; - if (isBuiltIn) { - subtitle = 'Using ${_builtInProviders[searchProvider]}'; - } else if (activeExtension != null) { - subtitle = context.l10n.optionsUsingExtension( - activeExtension.displayName, - ); - } else { - subtitle = context.l10n.optionsPrimaryProviderSubtitle; - } - - return Padding( - padding: const EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - context.l10n.optionsPrimaryProvider, - style: Theme.of( - context, - ).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w500), - ), - const SizedBox(height: 4), - Text( - subtitle, - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: hasNonDefaultProvider - ? colorScheme.primary - : colorScheme.onSurfaceVariant, - ), - ), - const SizedBox(height: 16), - Row( - children: [ - Expanded( - child: _SourceChip( - icon: Icons.graphic_eq, - label: defaultProviderLabel, - isSelected: searchProvider.isEmpty, - onTap: () { - if (hasNonDefaultProvider) { - ref - .read(settingsProvider.notifier) - .setSearchProvider(null); - } - }, - ), - ), - const SizedBox(width: 8), - Expanded( - child: _SourceChip( - icon: Icons.waves, - label: 'Tidal', - isSelected: searchProvider == 'tidal', - onTap: () { - ref - .read(settingsProvider.notifier) - .setSearchProvider('tidal'); - }, - ), - ), - const SizedBox(width: 8), - Expanded( - child: _SourceChip( - icon: Icons.album, - label: 'Qobuz', - isSelected: searchProvider == 'qobuz', - onTap: () { - ref - .read(settingsProvider.notifier) - .setSearchProvider('qobuz'); - }, - ), - ), - ], - ), - if (activeExtension != null) ...[ - const SizedBox(height: 12), - Row( - children: [ - Icon( - Icons.info_outline, - size: 16, - color: colorScheme.onSurfaceVariant, - ), - const SizedBox(width: 8), - Expanded( - child: Text( - 'Tap $defaultProviderLabel to switch back from extension', - style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: colorScheme.onSurfaceVariant, - ), - ), - ), - ], - ), - ], - ], - ), - ); - } -} - -class _DefaultSearchTabSelector extends ConsumerWidget { - const _DefaultSearchTabSelector(); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final colorScheme = Theme.of(context).colorScheme; - final selectedTab = ref.watch( - settingsProvider.select((s) => s.defaultSearchTab), - ); - - return Padding( - padding: const EdgeInsets.fromLTRB(16, 0, 16, 16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - context.l10n.optionsDefaultSearchTab, - style: Theme.of( - context, - ).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w500), - ), - const SizedBox(height: 4), - Text( - context.l10n.optionsDefaultSearchTabSubtitle, - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: colorScheme.onSurfaceVariant, - ), - ), - const SizedBox(height: 16), - Row( - children: [ - Expanded( - child: _SourceChip( - icon: Icons.dashboard_outlined, - label: context.l10n.historyFilterAll, - isSelected: selectedTab == 'all', - onTap: () => ref - .read(settingsProvider.notifier) - .setDefaultSearchTab('all'), - ), - ), - const SizedBox(width: 8), - Expanded( - child: _SourceChip( - icon: Icons.music_note, - label: context.l10n.searchSongs, - isSelected: selectedTab == 'track', - onTap: () => ref - .read(settingsProvider.notifier) - .setDefaultSearchTab('track'), - ), - ), - const SizedBox(width: 8), - Expanded( - child: _SourceChip( - icon: Icons.person, - label: context.l10n.searchArtists, - isSelected: selectedTab == 'artist', - onTap: () => ref - .read(settingsProvider.notifier) - .setDefaultSearchTab('artist'), - ), - ), - const SizedBox(width: 8), - Expanded( - child: _SourceChip( - icon: Icons.album, - label: context.l10n.searchAlbums, - isSelected: selectedTab == 'album', - onTap: () => ref - .read(settingsProvider.notifier) - .setDefaultSearchTab('album'), - ), - ), - ], - ), - ], - ), - ); - } -} - -class _SourceChip extends StatelessWidget { - final IconData icon; - final String label; - final bool isSelected; - final VoidCallback? onTap; - - const _SourceChip({ - required this.icon, - required this.label, - required this.isSelected, - this.onTap, - }); - - @override - Widget build(BuildContext context) { - final colorScheme = Theme.of(context).colorScheme; - final isDark = Theme.of(context).brightness == Brightness.dark; - - final unselectedColor = isDark - ? Color.alphaBlend( - Colors.white.withValues(alpha: 0.05), - colorScheme.surface, - ) - : colorScheme.surfaceContainerHigh; - - return Material( - color: isSelected ? colorScheme.primaryContainer : unselectedColor, - borderRadius: BorderRadius.circular(12), - child: InkWell( - onTap: onTap, - borderRadius: BorderRadius.circular(12), - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 14, horizontal: 18), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Icon( - icon, - size: 28, - color: isSelected - ? colorScheme.onPrimaryContainer - : colorScheme.onSurfaceVariant, - ), - const SizedBox(height: 6), - Text( - label, - style: TextStyle( - fontSize: 12, - fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal, - color: isSelected - ? colorScheme.onPrimaryContainer - : colorScheme.onSurfaceVariant, - ), - ), - ], - ), - ), - ), - ); - } -} diff --git a/lib/screens/settings/settings_tab.dart b/lib/screens/settings/settings_tab.dart index f08d967a..97b51288 100644 --- a/lib/screens/settings/settings_tab.dart +++ b/lib/screens/settings/settings_tab.dart @@ -4,9 +4,12 @@ import 'package:spotiflac_android/constants/app_info.dart'; import 'package:spotiflac_android/l10n/l10n.dart'; import 'package:spotiflac_android/screens/settings/appearance_settings_page.dart'; import 'package:spotiflac_android/screens/settings/download_settings_page.dart'; +import 'package:spotiflac_android/screens/settings/files_settings_page.dart'; +import 'package:spotiflac_android/screens/settings/lyrics_settings_page.dart'; +import 'package:spotiflac_android/screens/settings/metadata_settings_page.dart'; import 'package:spotiflac_android/screens/settings/extensions_page.dart'; import 'package:spotiflac_android/screens/settings/library_settings_page.dart'; -import 'package:spotiflac_android/screens/settings/options_settings_page.dart'; +import 'package:spotiflac_android/screens/settings/app_settings_page.dart'; import 'package:spotiflac_android/screens/settings/about_page.dart'; import 'package:spotiflac_android/screens/settings/cache_management_page.dart'; import 'package:spotiflac_android/screens/settings/donate_page.dart'; @@ -48,7 +51,7 @@ class SettingsTab extends ConsumerWidget { title: Text( context.l10n.settingsTitle, style: TextStyle( - fontSize: 20 + (14 * expandRatio), // 20 -> 34 + fontSize: 20 + (14 * expandRatio), fontWeight: FontWeight.bold, color: colorScheme.onSurface, ), @@ -58,6 +61,7 @@ class SettingsTab extends ConsumerWidget { ), ), + // ── Group 1: Appearance & Content ────────────────────────────── SliverToBoxAdapter( child: Builder( builder: (context) { @@ -72,6 +76,34 @@ class SettingsTab extends ConsumerWidget { onTap: () => _navigateTo(context, const AppearanceSettingsPage()), ), + SettingsItem( + icon: Icons.library_music_outlined, + title: l10n.settingsLocalLibrary, + subtitle: l10n.settingsLocalLibrarySubtitle, + onTap: () => + _navigateTo(context, const LibrarySettingsPage()), + ), + SettingsItem( + icon: Icons.extension_outlined, + title: l10n.settingsExtensions, + subtitle: l10n.settingsExtensionsSubtitle, + onTap: () => _navigateTo(context, const ExtensionsPage()), + showDivider: false, + ), + ], + ); + }, + ), + ), + + // ── Group 2: Download ────────────────────────────────────────── + SliverToBoxAdapter( + child: Builder( + builder: (context) { + final l10n = context.l10n; + return SettingsGroup( + margin: const EdgeInsets.fromLTRB(16, 4, 16, 4), + children: [ SettingsItem( icon: Icons.download_outlined, title: l10n.settingsDownload, @@ -80,12 +112,41 @@ class SettingsTab extends ConsumerWidget { _navigateTo(context, const DownloadSettingsPage()), ), SettingsItem( - icon: Icons.library_music_outlined, - title: l10n.settingsLocalLibrary, - subtitle: l10n.settingsLocalLibrarySubtitle, + icon: Icons.folder_outlined, + title: l10n.settingsFiles, + subtitle: l10n.settingsFilesSubtitle, onTap: () => - _navigateTo(context, const LibrarySettingsPage()), + _navigateTo(context, const FilesSettingsPage()), ), + SettingsItem( + icon: Icons.sell_outlined, + title: l10n.settingsMetadata, + subtitle: l10n.settingsMetadataSubtitle, + onTap: () => + _navigateTo(context, const MetadataSettingsPage()), + ), + SettingsItem( + icon: Icons.lyrics_outlined, + title: l10n.settingsLyrics, + subtitle: l10n.settingsLyricsSubtitle, + onTap: () => + _navigateTo(context, const LyricsSettingsPage()), + showDivider: false, + ), + ], + ); + }, + ), + ), + + // ── Group 3: App ─────────────────────────────────────────────── + SliverToBoxAdapter( + child: Builder( + builder: (context) { + final l10n = context.l10n; + return SettingsGroup( + margin: const EdgeInsets.fromLTRB(16, 4, 16, 4), + children: [ SettingsItem( icon: Icons.storage_outlined, title: l10n.settingsCache, @@ -95,41 +156,22 @@ class SettingsTab extends ConsumerWidget { ), SettingsItem( icon: Icons.tune_outlined, - title: l10n.settingsOptions, - subtitle: l10n.settingsOptionsSubtitle, + title: l10n.settingsApp, + subtitle: l10n.settingsAppSubtitle, onTap: () => - _navigateTo(context, const OptionsSettingsPage()), + _navigateTo(context, const AppSettingsPage()), ), SettingsItem( - icon: Icons.extension_outlined, - title: l10n.settingsExtensions, - subtitle: l10n.settingsExtensionsSubtitle, - onTap: () => _navigateTo(context, const ExtensionsPage()), + icon: Icons.article_outlined, + title: l10n.logTitle, + subtitle: l10n.settingsLogsSubtitle, + onTap: () => _navigateTo(context, const LogScreen()), ), SettingsItem( icon: Icons.favorite_outline, title: l10n.settingsDonate, subtitle: l10n.settingsDonateSubtitle, onTap: () => _navigateTo(context, const DonatePage()), - showDivider: false, - ), - ], - ); - }, - ), - ), - - SliverToBoxAdapter( - child: Builder( - builder: (context) { - final l10n = context.l10n; - return SettingsGroup( - children: [ - SettingsItem( - icon: Icons.article_outlined, - title: l10n.logTitle, - subtitle: l10n.settingsLogsSubtitle, - onTap: () => _navigateTo(context, const LogScreen()), ), SettingsItem( icon: Icons.info_outline, From 6e9fa459151f99b1c4e31e43ccb75ee1b5b058af Mon Sep 17 00:00:00 2001 From: Amonoman Date: Tue, 28 Apr 2026 13:46:44 +0200 Subject: [PATCH 3/4] feat(settings): reorganize settings into focused pages - split download/options into download, files, metadata, lyrics, app - add dedicated pages for files & folders, metadata, and lyrics - move search source and fallback into download page - replace options_settings_page with app_settings_page - add missing l10n keys for all new pages - improve subtitle copy for download, embed lyrics, primary provider --- lib/l10n/arb/app_en.arb | 289 +++++++++++++++++++++++----------------- 1 file changed, 165 insertions(+), 124 deletions(-) diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index e3f4a6d0..a5d4d5ac 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -1,6 +1,6 @@ { "@@locale": "en", - "@@last_modified": "2026-01-16", + "@@last_modified": "2026-04-28", "appName": "SpotiFLAC", "@appName": { "description": "App name - DO NOT TRANSLATE" @@ -99,7 +99,7 @@ }, "downloadFolderOrganization": "Folder Organization", "@downloadFolderOrganization": { - "description": "Setting for folder structure" + "description": "Title of the folder organization picker bottom sheet" }, "appearanceTitle": "Appearance", "@appearanceTitle": { @@ -145,7 +145,7 @@ "@optionsPrimaryProvider": { "description": "Main search provider setting" }, - "optionsPrimaryProviderSubtitle": "Service used when searching by track name.", + "optionsPrimaryProviderSubtitle": "Service used for searching by track or album name", "@optionsPrimaryProviderSubtitle": { "description": "Subtitle for primary provider" }, @@ -194,7 +194,7 @@ "@optionsEmbedLyrics": { "description": "Embed lyrics in audio files" }, - "optionsEmbedLyricsSubtitle": "Embed synced lyrics into FLAC files", + "optionsEmbedLyricsSubtitle": "Save synced lyrics alongside your downloaded tracks", "@optionsEmbedLyricsSubtitle": { "description": "Subtitle for embed lyrics" }, @@ -729,7 +729,7 @@ }, "dialogDownload": "Download", "@dialogDownload": { - "description": "Dialog button - download action" + "description": "Confirm button in Download All dialog" }, "dialogDiscard": "Discard", "@dialogDiscard": { @@ -1449,11 +1449,11 @@ "@settingsAppearanceSubtitle": { "description": "Appearance settings description" }, - "settingsDownloadSubtitle": "Service, quality, filename format", + "settingsDownloadSubtitle": "Service, quality, fallback", "@settingsDownloadSubtitle": { "description": "Download settings description" }, - "settingsOptionsSubtitle": "Fallback, lyrics, cover art, updates", + "settingsOptionsSubtitle": "Fallback, metadata, lyrics, cover art", "@settingsOptionsSubtitle": { "description": "Options settings description" }, @@ -3592,18 +3592,17 @@ } } }, - "downloadUseAlbumArtistForFoldersAlbumSubtitle": "Artist folders use Album Artist when available", + "downloadUseAlbumArtistForFoldersAlbumSubtitle": "Folder named after Album Artist tag", "@downloadUseAlbumArtistForFoldersAlbumSubtitle": { - "description": "Subtitle when Album Artist is used for folder naming" + "description": "Subtitle when album artist is used for folder names" }, - "downloadUseAlbumArtistForFoldersTrackSubtitle": "Artist folders use Track Artist only", + "downloadUseAlbumArtistForFoldersTrackSubtitle": "Folder named after Track Artist tag", "@downloadUseAlbumArtistForFoldersTrackSubtitle": { - "description": "Subtitle when Track Artist is used for folder naming" + "description": "Subtitle when track artist is used for folder names" }, - - "lyricsProvidersTitle": "Lyrics Providers", + "lyricsProvidersTitle": "Lyrics Provider Priority", "@lyricsProvidersTitle": { - "description": "Title for the lyrics provider priority page" + "description": "Settings item title for lyrics provider order" }, "lyricsProvidersDescription": "Enable, disable and reorder lyrics sources. Providers are tried top-to-bottom until lyrics are found.", "@lyricsProvidersDescription": { @@ -3667,7 +3666,6 @@ "@lyricsProviderExtensionDesc": { "description": "Generic description for extension-based lyrics providers" }, - "safMigrationTitle": "Storage Update Required", "@safMigrationTitle": { "description": "Title of SAF migration dialog" @@ -3684,16 +3682,14 @@ "@safMigrationSuccess": { "description": "Snackbar after successfully migrating to SAF" }, - - "settingsDonate": "Donate", + "settingsDonate": "Support Development", "@settingsDonate": { - "description": "Settings menu item - donate" + "description": "Settings menu item - donate page" }, - "settingsDonateSubtitle": "Support SpotiFLAC-Mobile development", + "settingsDonateSubtitle": "Buy the developer a coffee", "@settingsDonateSubtitle": { "description": "Subtitle for donate menu item" }, - "tooltipLoveAll": "Love All", "@tooltipLoveAll": { "description": "Tooltip for the Love All button on album/playlist screens" @@ -3720,10 +3716,9 @@ } } }, - "dialogDownloadAllTitle": "Download All", "@dialogDownloadAllTitle": { - "description": "Title of the Download All confirmation dialog" + "description": "Dialog title for bulk download confirmation" }, "dialogDownloadAllMessage": "Download {count} tracks?", "@dialogDownloadAllMessage": { @@ -3734,11 +3729,6 @@ } } }, - "dialogDownload": "Download", - "@dialogDownload": { - "description": "Confirm button in Download All dialog" - }, - "homeSkipAlreadyDownloaded": "Skip already downloaded songs", "@homeSkipAlreadyDownloaded": { "description": "Checkbox label in import dialog to skip already-downloaded songs" @@ -3751,7 +3741,6 @@ "@homeAlbumInfoUnavailable": { "description": "Snackbar when album info cannot be loaded" }, - "snackbarLoadingCueSheet": "Loading CUE sheet...", "@snackbarLoadingCueSheet": { "description": "Snackbar while loading a CUE sheet file" @@ -3781,190 +3770,181 @@ "@snackbarNoActionDefined": { "description": "Snackbar when an extension button has no action configured" }, - "noTracksFoundForAlbum": "No tracks found for this album", "@noTracksFoundForAlbum": { "description": "Empty state message when an album has no tracks" }, - - "downloadLocationSubtitle": "Choose storage mode for downloaded files.", + "downloadLocationSubtitle": "Choose where to save your downloaded tracks", "@downloadLocationSubtitle": { - "description": "Subtitle text in Android download location bottom sheet" + "description": "Subtitle shown in the download location picker sheet" }, - "storageModeAppFolder": "App folder (non-SAF)", + "storageModeAppFolder": "App Folder (Recommended)", "@storageModeAppFolder": { - "description": "Storage mode option - use legacy app folder" + "description": "Storage mode option - app-managed folder" }, - "storageModeAppFolderSubtitle": "Use default Music/SpotiFLAC path", + "storageModeAppFolderSubtitle": "Saves to Music/SpotiFLAC by default", "@storageModeAppFolderSubtitle": { "description": "Subtitle for app folder storage mode" }, - "storageModeSaf": "SAF folder", + "storageModeSaf": "Custom Folder (SAF)", "@storageModeSaf": { - "description": "Storage mode option - use Android SAF picker" + "description": "Storage mode option - Storage Access Framework" }, - "storageModeSafSubtitle": "Pick folder via Android Storage Access Framework", + "storageModeSafSubtitle": "Pick any folder, including SD card", "@storageModeSafSubtitle": { "description": "Subtitle for SAF storage mode" }, - "downloadFilenameDescription": "Customize how your files are named.", + "downloadFilenameDescription": "Use {artist}, {title}, {album}, {track}, {year}, {date}, {disc} as placeholders.", "@downloadFilenameDescription": { - "description": "Description text in filename format bottom sheet" + "description": "Description shown in filename format editor" }, "downloadFilenameInsertTag": "Tap to insert tag:", "@downloadFilenameInsertTag": { "description": "Label above filename tag chips" }, - "downloadSeparateSinglesEnabled": "Albums/ and Singles/ folders", + "downloadSeparateSinglesEnabled": "Singles and EPs saved in a separate folder", "@downloadSeparateSinglesEnabled": { - "description": "Subtitle when separate singles folder is enabled" + "description": "Subtitle when separate singles folder is on" }, - "downloadSeparateSinglesDisabled": "All files in same structure", + "downloadSeparateSinglesDisabled": "Singles and albums saved in the same folder", "@downloadSeparateSinglesDisabled": { - "description": "Subtitle when separate singles folder is disabled" + "description": "Subtitle when separate singles folder is off" }, "downloadArtistNameFilters": "Artist Name Filters", "@downloadArtistNameFilters": { "description": "Setting title for artist folder filter options" }, - "downloadCreatePlaylistSourceFolder": "Create playlist source folder", + "downloadCreatePlaylistSourceFolder": "Playlist Source Folder", "@downloadCreatePlaylistSourceFolder": { - "description": "Setting title for adding a playlist folder prefix before the normal organization structure" + "description": "Setting to create a subfolder per playlist source" }, - "downloadCreatePlaylistSourceFolderEnabled": "Playlist downloads use Playlist/ plus your normal folder structure.", + "downloadCreatePlaylistSourceFolderEnabled": "A subfolder is created for each playlist", "@downloadCreatePlaylistSourceFolderEnabled": { - "description": "Subtitle when playlist source folder prefix is enabled" + "description": "Subtitle when playlist folder is enabled" }, - "downloadCreatePlaylistSourceFolderDisabled": "Playlist downloads use the normal folder structure only.", + "downloadCreatePlaylistSourceFolderDisabled": "All tracks saved directly to download folder", "@downloadCreatePlaylistSourceFolderDisabled": { - "description": "Subtitle when playlist source folder prefix is disabled" + "description": "Subtitle when playlist folder is disabled" }, - "downloadCreatePlaylistSourceFolderRedundant": "By Playlist already places downloads inside a playlist folder.", + "downloadCreatePlaylistSourceFolderRedundant": "Handled by folder organization setting", "@downloadCreatePlaylistSourceFolderRedundant": { - "description": "Subtitle when playlist folder prefix setting is redundant because folder organization is already by playlist" + "description": "Subtitle when folder organization is already set to playlist" }, "downloadSongLinkRegion": "SongLink Region", "@downloadSongLinkRegion": { - "description": "Setting title for SongLink country region" + "description": "Setting for SongLink region used during fallback resolution" }, - "downloadNetworkCompatibilityMode": "Network compatibility mode", + "downloadNetworkCompatibilityMode": "Network Compatibility Mode", "@downloadNetworkCompatibilityMode": { - "description": "Setting title for network compatibility toggle" + "description": "Setting for legacy TLS/network handling" }, - "downloadNetworkCompatibilityModeEnabled": "Enabled: try HTTP + accept invalid TLS certificates (unsafe)", + "downloadNetworkCompatibilityModeEnabled": "Using legacy TLS settings for older networks", "@downloadNetworkCompatibilityModeEnabled": { - "description": "Subtitle when network compatibility mode is enabled" + "description": "Subtitle when network compatibility mode is on" }, - "downloadNetworkCompatibilityModeDisabled": "Off: strict HTTPS certificate validation (recommended)", + "downloadNetworkCompatibilityModeDisabled": "Using standard network settings", "@downloadNetworkCompatibilityModeDisabled": { - "description": "Subtitle when network compatibility mode is disabled" + "description": "Subtitle when network compatibility mode is off" }, - "downloadSelectServiceToEnable": "Select a built-in service to enable", + "downloadSelectServiceToEnable": "Select Tidal or Qobuz to enable this option", "@downloadSelectServiceToEnable": { - "description": "Hint shown instead of Ask-quality subtitle when no built-in service selected" + "description": "Subtitle when quality picker is disabled due to extension service" }, - - "downloadSelectTidalQobuz": "Select Tidal or Qobuz above to configure quality", + "downloadSelectTidalQobuz": "Select Tidal or Qobuz to choose audio quality", "@downloadSelectTidalQobuz": { - "description": "Info hint when non-Tidal/Qobuz service is selected" + "description": "Info shown when a non-built-in service is selected" }, - "downloadEmbedLyricsDisabled": "Disabled while Embed Metadata is turned off", + "downloadEmbedLyricsDisabled": "Enable metadata embedding first", "@downloadEmbedLyricsDisabled": { - "description": "Subtitle for Embed Lyrics when Embed Metadata is disabled" + "description": "Subtitle when lyrics embedding is blocked by metadata toggle" }, "downloadNeteaseIncludeTranslation": "Netease: Include Translation", "@downloadNeteaseIncludeTranslation": { - "description": "Toggle title for including Netease translated lyrics" + "description": "Setting to include translated lyrics from Netease" }, - "downloadNeteaseIncludeTranslationEnabled": "Append translated lyrics when available", + "downloadNeteaseIncludeTranslationEnabled": "Chinese translation lines included", "@downloadNeteaseIncludeTranslationEnabled": { - "description": "Subtitle when Netease translation is enabled" + "description": "Subtitle when Netease translation is on" }, - "downloadNeteaseIncludeTranslationDisabled": "Use original lyrics only", + "downloadNeteaseIncludeTranslationDisabled": "Original lyrics only", "@downloadNeteaseIncludeTranslationDisabled": { - "description": "Subtitle when Netease translation is disabled" + "description": "Subtitle when Netease translation is off" }, "downloadNeteaseIncludeRomanization": "Netease: Include Romanization", "@downloadNeteaseIncludeRomanization": { - "description": "Toggle title for including Netease romanized lyrics" + "description": "Setting to include romanized lyrics from Netease" }, - "downloadNeteaseIncludeRomanizationEnabled": "Append romanized lyrics when available", + "downloadNeteaseIncludeRomanizationEnabled": "Romanization lines included", "@downloadNeteaseIncludeRomanizationEnabled": { - "description": "Subtitle when Netease romanization is enabled" + "description": "Subtitle when Netease romanization is on" }, - "downloadNeteaseIncludeRomanizationDisabled": "Disabled", + "downloadNeteaseIncludeRomanizationDisabled": "No romanization", "@downloadNeteaseIncludeRomanizationDisabled": { - "description": "Subtitle when Netease romanization is disabled" + "description": "Subtitle when Netease romanization is off" }, - "downloadAppleQqMultiPerson": "Apple/QQ Multi-Person Word-by-Word", + "downloadAppleQqMultiPerson": "Apple / QQ: Multi-Person Lyrics", "@downloadAppleQqMultiPerson": { - "description": "Toggle title for Apple/QQ multi-person word-by-word lyrics" + "description": "Setting for word-by-word multi-person lyrics from Apple Music and QQ Music" }, - "downloadAppleQqMultiPersonEnabled": "Enable v1/v2 speaker and [bg:] tags", + "downloadAppleQqMultiPersonEnabled": "Speaker labels included for duets and group tracks", "@downloadAppleQqMultiPersonEnabled": { - "description": "Subtitle when multi-person word-by-word is enabled" + "description": "Subtitle when multi-person lyrics is on" }, - "downloadAppleQqMultiPersonDisabled": "Simplified word-by-word formatting", + "downloadAppleQqMultiPersonDisabled": "Standard lyrics without speaker labels", "@downloadAppleQqMultiPersonDisabled": { - "description": "Subtitle when multi-person word-by-word is disabled" + "description": "Subtitle when multi-person lyrics is off" }, "downloadMusixmatchLanguage": "Musixmatch Language", "@downloadMusixmatchLanguage": { - "description": "Setting title for Musixmatch language preference" + "description": "Setting for Musixmatch lyrics translation language" }, - "downloadMusixmatchLanguageAuto": "Auto (original)", + "downloadMusixmatchLanguageAuto": "Auto (original language)", "@downloadMusixmatchLanguageAuto": { - "description": "Option label when Musixmatch uses original language" + "description": "Subtitle when no language is set" }, - "downloadFilterContributing": "Filter contributing artists in Album Artist", + "downloadFilterContributing": "Filter Contributing Artists", "@downloadFilterContributing": { - "description": "Toggle title for filtering contributing artists in Album Artist metadata" + "description": "Setting to strip contributing artists from Album Artist folder name" }, - "downloadFilterContributingEnabled": "Album Artist metadata uses primary artist only", + "downloadFilterContributingEnabled": "Contributing artists removed from Album Artist folder name", "@downloadFilterContributingEnabled": { - "description": "Subtitle when contributing artist filter is enabled" + "description": "Subtitle when contributing artist filter is on" }, - "downloadFilterContributingDisabled": "Keep full Album Artist metadata value", + "downloadFilterContributingDisabled": "Full Album Artist string used", "@downloadFilterContributingDisabled": { - "description": "Subtitle when contributing artist filter is disabled" + "description": "Subtitle when contributing artist filter is off" }, - - "downloadProvidersNoneEnabled": "None enabled", + "downloadProvidersNoneEnabled": "No providers enabled", "@downloadProvidersNoneEnabled": { - "description": "Subtitle for lyrics providers setting when no providers are enabled" + "description": "Shown when no lyrics providers are active" }, "downloadMusixmatchLanguageCode": "Language code", "@downloadMusixmatchLanguageCode": { - "description": "Label for the Musixmatch language code text field" + "description": "Label for Musixmatch language input field" }, - "downloadMusixmatchLanguageHint": "auto / en / es / ja", + "downloadMusixmatchLanguageHint": "e.g. en, de, ja", "@downloadMusixmatchLanguageHint": { - "description": "Hint text for the Musixmatch language code field" + "description": "Placeholder for Musixmatch language input" }, - "downloadMusixmatchLanguageDesc": "Set preferred language code (example: en, es, ja). Leave empty for auto.", + "downloadMusixmatchLanguageDesc": "Enter a BCP-47 language code (e.g. en, de, ja) to request translated lyrics from Musixmatch.", "@downloadMusixmatchLanguageDesc": { - "description": "Description in the Musixmatch language picker" + "description": "Description in Musixmatch language picker" }, "downloadMusixmatchAuto": "Auto", "@downloadMusixmatchAuto": { - "description": "Button to reset Musixmatch language to automatic" + "description": "Button to clear Musixmatch language (use auto)" }, - - "downloadNetworkAnySubtitle": "WiFi + Mobile Data", + "downloadNetworkAnySubtitle": "Use WiFi or mobile data", "@downloadNetworkAnySubtitle": { - "description": "Subtitle for 'Any' network mode option" + "description": "Subtitle for any-network option in picker" }, - "downloadNetworkWifiOnlySubtitle": "Pause downloads on mobile data", + "downloadNetworkWifiOnlySubtitle": "Downloads pause when on mobile data", "@downloadNetworkWifiOnlySubtitle": { - "description": "Subtitle for 'WiFi only' network mode option" + "description": "Subtitle for WiFi-only option in picker" }, - "downloadSongLinkRegionDesc": "Used as userCountry for SongLink API lookup.", + "downloadSongLinkRegionDesc": "Region used when resolving track links via SongLink. Choose the country where your streaming services are available.", "@downloadSongLinkRegionDesc": { - "description": "Description in the SongLink region picker" - }, - "downloadFolderOrganization": "Folder Organization", - "@downloadFolderOrganization": { - "description": "Title of the folder organization picker bottom sheet" + "description": "Description in SongLink region picker" }, "snackbarUnsupportedAudioFormat": "Unsupported audio format", "@snackbarUnsupportedAudioFormat": { @@ -3974,10 +3954,6 @@ "@cacheRefresh": { "description": "Tooltip for refresh button on cache management page" }, - "dialogDownloadAllTitle": "Download All", - "@dialogDownloadAllTitle": { - "description": "Dialog title for bulk download confirmation" - }, "dialogDownloadPlaylistsMessage": "Download {trackCount} {trackCount, plural, =1{track} other{tracks}} from {playlistCount} {playlistCount, plural, =1{playlist} other{playlists}}?", "@dialogDownloadPlaylistsMessage": { "description": "Dialog message for bulk playlist download confirmation", @@ -4105,7 +4081,6 @@ "@editMetadataSelectEmpty": { "description": "Button to select only fields that are currently empty" }, - "queueDownloadingCount": "Downloading ({count})", "@queueDownloadingCount": { "description": "Header for active downloads section with count", @@ -4229,7 +4204,6 @@ "@audioAnalysisSamples": { "description": "Total samples metric label" }, - "extensionsSearchWith": "Search with {providerName}", "@extensionsSearchWith": { "description": "Extensions page - subtitle for built-in search provider option", @@ -4249,7 +4223,7 @@ }, "extensionsHomeFeedAuto": "Auto", "@extensionsHomeFeedAuto": { - "description": "Extensions page - home feed provider option: auto" + "description": "Label for auto-selected search provider" }, "extensionsHomeFeedAutoSubtitle": "Automatically select the best available", "@extensionsHomeFeedAutoSubtitle": { @@ -4268,7 +4242,6 @@ "@extensionsNoHomeFeedExtensions": { "description": "Extensions page - shown when no installed extension has home feed" }, - "sortAlphaAsc": "A-Z", "@sortAlphaAsc": { "description": "Sort option - alphabetical ascending" @@ -4294,7 +4267,6 @@ "@cancelDownloadKeep": { "description": "Dialog button - keep the active download (do not cancel)" }, - "metadataSaveFailedFfmpeg": "Failed to save metadata via FFmpeg", "@metadataSaveFailedFfmpeg": { "description": "Snackbar error when FFmpeg fails to write metadata" @@ -4303,7 +4275,6 @@ "@metadataSaveFailedStorage": { "description": "Snackbar error when writing metadata file back to storage fails" }, - "snackbarFolderPickerFailed": "Failed to open folder picker: {error}", "@snackbarFolderPickerFailed": { "description": "Snackbar shown when folder picker fails to open", @@ -4313,7 +4284,6 @@ } } }, - "errorLoadAlbum": "Failed to load album", "@errorLoadAlbum": { "description": "Error state shown when album fails to load" @@ -4326,7 +4296,6 @@ "@errorLoadArtist": { "description": "Error state shown when artist fails to load" }, - "notifChannelDownloadName": "Download Progress", "@notifChannelDownloadName": { "description": "Android notification channel name for download progress" @@ -4540,5 +4509,77 @@ "notifUpdateFailedBody": "Could not download update. Try again later.", "@notifUpdateFailedBody": { "description": "Notification body when app update download fails" + }, + "settingsFiles": "Files & Folders", + "@settingsFiles": { + "description": "Settings menu item - file and folder settings" + }, + "settingsFilesSubtitle": "Download location, filename, folder structure", + "@settingsFilesSubtitle": { + "description": "Subtitle for files & folders settings" + }, + "settingsMetadata": "Metadata", + "@settingsMetadata": { + "description": "Settings menu item - metadata settings" + }, + "settingsMetadataSubtitle": "Cover art, tags, ReplayGain, providers", + "@settingsMetadataSubtitle": { + "description": "Subtitle for metadata settings" + }, + "settingsLyrics": "Lyrics", + "@settingsLyrics": { + "description": "Settings menu item - lyrics settings" + }, + "settingsLyricsSubtitle": "Embed, mode, providers, language options", + "@settingsLyricsSubtitle": { + "description": "Subtitle for lyrics settings" + }, + "settingsApp": "App", + "@settingsApp": { + "description": "Settings menu item - app settings" + }, + "settingsAppSubtitle": "Updates, data, extension repo, debug", + "@settingsAppSubtitle": { + "description": "Subtitle for app settings" + }, + "sectionMetadataProviders": "Providers", + "@sectionMetadataProviders": { + "description": "Settings section header for metadata providers" + }, + "sectionDuplicates": "Duplicates", + "@sectionDuplicates": { + "description": "Settings section header for deduplication" + }, + "sectionLyricsProviderOptions": "Provider Options", + "@sectionLyricsProviderOptions": { + "description": "Settings section header for per-provider lyrics options" + }, + "metadataProvidersTitle": "Metadata Provider Priority", + "@metadataProvidersTitle": { + "description": "Settings item title for metadata provider order" + }, + "metadataProvidersSubtitle": "Drag to set search and metadata source order", + "@metadataProvidersSubtitle": { + "description": "Subtitle for metadata provider priority item" + }, + "downloadDeduplication": "Skip Duplicate Downloads", + "@downloadDeduplication": { + "description": "Setting - skip tracks already in download history" + }, + "downloadDeduplicationEnabled": "Already-downloaded tracks will be skipped", + "@downloadDeduplicationEnabled": { + "description": "Subtitle when deduplication is on" + }, + "downloadDeduplicationDisabled": "All tracks will be downloaded regardless of history", + "@downloadDeduplicationDisabled": { + "description": "Subtitle when deduplication is off" + }, + "downloadFallbackExtensions": "Fallback Extensions", + "@downloadFallbackExtensions": { + "description": "Settings item for configuring fallback extension providers" + }, + "downloadFallbackExtensionsSubtitle": "Choose which extensions can be used as fallback", + "@downloadFallbackExtensionsSubtitle": { + "description": "Subtitle for fallback extensions item" } -} +} \ No newline at end of file From bb0cc23461a5d45ac6087f94f62cae264cbd295b Mon Sep 17 00:00:00 2001 From: Amonoman Date: Sat, 2 May 2026 18:13:04 +0200 Subject: [PATCH 4/4] i18n: sync missing EN strings to all locales & fix DE consistency MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add 18 missing keys to DE, ES_ES, FR, HI, ID, JA, KO, NL, PT_PT, RU, TR, UK, ZH_CN, ZH_TW - Add 580 missing keys to ES, PT, ZH (outdated partial files) - Fix DE: Sie→du throughout, typos (Standart, auwählen), grammar errors in dialogs --- lib/l10n/arb/app_de.arb | 110 +- lib/l10n/arb/app_es.arb | 2853 ++++++++++++++++++++++++++++++++++++ lib/l10n/arb/app_es_ES.arb | 72 + lib/l10n/arb/app_fr.arb | 72 + lib/l10n/arb/app_hi.arb | 72 + lib/l10n/arb/app_id.arb | 72 + lib/l10n/arb/app_ja.arb | 72 + lib/l10n/arb/app_ko.arb | 72 + lib/l10n/arb/app_nl.arb | 72 + lib/l10n/arb/app_pt.arb | 2853 ++++++++++++++++++++++++++++++++++++ lib/l10n/arb/app_pt_PT.arb | 72 + lib/l10n/arb/app_ru.arb | 72 + lib/l10n/arb/app_tr.arb | 72 + lib/l10n/arb/app_uk.arb | 72 + lib/l10n/arb/app_zh.arb | 2853 ++++++++++++++++++++++++++++++++++++ lib/l10n/arb/app_zh_CN.arb | 72 + lib/l10n/arb/app_zh_TW.arb | 72 + 17 files changed, 9586 insertions(+), 19 deletions(-) diff --git a/lib/l10n/arb/app_de.arb b/lib/l10n/arb/app_de.arb index 188f5d34..33150a80 100644 --- a/lib/l10n/arb/app_de.arb +++ b/lib/l10n/arb/app_de.arb @@ -121,7 +121,7 @@ "@appearanceDynamicColor": { "description": "Material You dynamic colors" }, - "appearanceDynamicColorSubtitle": "Farben von Ihrem Hintergrundbild verwenden", + "appearanceDynamicColorSubtitle": "Farben deines Hintergrundbilds verwenden", "@appearanceDynamicColorSubtitle": { "description": "Subtitle for dynamic color" }, @@ -178,7 +178,7 @@ "@optionsAutoFallbackSubtitle": { "description": "Subtitle for auto fallback" }, - "optionsUseExtensionProviders": "Erweiterungs-Anbieter verwenden", + "optionsUseExtensionProviders": "Erweiterungsanbieter verwenden", "@optionsUseExtensionProviders": { "description": "Enable extension download providers" }, @@ -194,7 +194,7 @@ "@optionsEmbedLyrics": { "description": "Embed lyrics in audio files" }, - "optionsEmbedLyricsSubtitle": "Synchronisierte Lyrics in FLAC-Dateien einbetten", + "optionsEmbedLyricsSubtitle": "Synchronisierte Songtexte neben heruntergeladenen Titeln speichern", "@optionsEmbedLyricsSubtitle": { "description": "Subtitle for embed lyrics" }, @@ -230,7 +230,7 @@ "@optionsArtistTagModeJoined": { "description": "Artist tag mode option that joins multiple artists into one value" }, - "optionsArtistTagModeJoinedSubtitle": "Einen Künstler wert wie \"Artist A, Artist B\" für maximale Spieler-Kompatibilität schreiben.", + "optionsArtistTagModeJoinedSubtitle": "Einen Künstler-Wert wie \"Künstler A, Künstler B\" für maximale Player-Kompatibilität schreiben.", "@optionsArtistTagModeJoinedSubtitle": { "description": "Subtitle for joined artist tag mode" }, @@ -438,7 +438,7 @@ "@aboutReportIssue": { "description": "Link to report bugs" }, - "aboutReportIssueSubtitle": "Melde jedes Problem, die dir auftreten", + "aboutReportIssueSubtitle": "Melde Probleme, die dir auffallen", "@aboutReportIssueSubtitle": { "description": "Subtitle for report issue" }, @@ -486,7 +486,7 @@ "@aboutSachinsenalDesc": { "description": "Credit description for sachinsenal0x64" }, - "aboutSjdonadoDesc": "Ersteller von I Don't Have Spotify (IDHS). Der Fallback-Link-Resolver, der den Tag rettete!", + "aboutSjdonadoDesc": "Ersteller von I Don't Have Spotify (IDHS). Der Fallback-Link-Resolver, der den Tag rettet!", "@aboutSjdonadoDesc": { "description": "Credit description for sjdonado" }, @@ -576,7 +576,7 @@ "@setupPermissionDeniedMessage": { "description": "Error when permission denied" }, - "setupPermissionRequired": "{permissionType} Zugriff verweigert", + "setupPermissionRequired": "{permissionType}-Berechtigung erforderlich", "@setupPermissionRequired": { "description": "Generic permission required title", "placeholders": { @@ -586,7 +586,7 @@ } } }, - "setupPermissionRequiredMessage": "{permissionType} Berechtigung ist erforderlich für\ndie beste Benutzererfahrung. Für kannst dies später in den Einstellungen ändern.", + "setupPermissionRequiredMessage": "{permissionType}-Berechtigung ist erforderlich für\ndie beste Benutzererfahrung. Du kannst dies später in den Einstellungen ändern.", "@setupPermissionRequiredMessage": { "description": "Generic permission required message", "placeholders": { @@ -603,7 +603,7 @@ "@setupNoFolderSelected": { "description": "Prompt when no folder selected" }, - "setupUseDefault": "Standart benutzen", + "setupUseDefault": "Standard verwenden", "@setupUseDefault": { "description": "Button to use default folder" }, @@ -663,7 +663,7 @@ "@setupNotificationEnable": { "description": "Button to enable notifications" }, - "setupFolderChoose": "Speicherort auwählen", + "setupFolderChoose": "Speicherort auswählen", "@setupFolderChoose": { "description": "Button to choose folder" }, @@ -747,7 +747,7 @@ "@dialogDiscardChanges": { "description": "Dialog title - unsaved changes warning" }, - "dialogUnsavedChanges": "Hast du noch nicht alle Änderungen gespeichert. Möchtest du die Änderungen verwerfen?", + "dialogUnsavedChanges": "Du hast ungespeicherte Änderungen. Möchtest du sie verwerfen?", "@dialogUnsavedChanges": { "description": "Dialog message - unsaved changes" }, @@ -759,7 +759,7 @@ "@dialogRemoveExtension": { "description": "Dialog title - uninstall extension" }, - "dialogRemoveExtensionMessage": "Bist Du sicher, dass Du diese Erweiterung entfernen möchtest? Diese Aktion kann nicht rückgängig gemacht werden.", + "dialogRemoveExtensionMessage": "Bist du sicher, dass du diese Erweiterung entfernen möchtest? Diese Aktion kann nicht rückgängig gemacht werden.", "@dialogRemoveExtensionMessage": { "description": "Dialog message - uninstall confirmation" }, @@ -767,7 +767,7 @@ "@dialogUninstallExtension": { "description": "Dialog title - uninstall extension" }, - "dialogUninstallExtensionMessage": "Bist du dir sicher, dass du {extensionName} entfernen möchtest?", + "dialogUninstallExtensionMessage": "Bist du sicher, dass du {extensionName} entfernen möchtest?", "@dialogUninstallExtensionMessage": { "description": "Dialog message - uninstall specific extension", "placeholders": { @@ -780,7 +780,7 @@ "@dialogClearHistoryTitle": { "description": "Dialog title - clear download history" }, - "dialogClearHistoryMessage": "Bist du dir sicher, dass du den gesamten Download verlauf löschen möchten? Dies kann nicht rückgängig gemacht werden.", + "dialogClearHistoryMessage": "Bist du sicher, dass du den gesamten Downloadverlauf löschen möchtest? Dies kann nicht rückgängig gemacht werden.", "@dialogClearHistoryMessage": { "description": "Dialog message - clear history confirmation" }, @@ -846,7 +846,7 @@ } } }, - "snackbarAlreadyInLibrary": "\"{trackName}\" existiert bereits in Ihrer Bibliothek", + "snackbarAlreadyInLibrary": "\"{trackName}\" existiert bereits in deiner Bibliothek", "@snackbarAlreadyInLibrary": { "description": "Snackbar - track already exists in local library", "placeholders": { @@ -867,7 +867,7 @@ "@snackbarCredentialsCleared": { "description": "Snackbar - Spotify credentials removed" }, - "snackbarDeletedTracks": "{count} {count, plural, one {}=1{Titel} other{Titel}}", + "snackbarDeletedTracks": "{count} {count, plural, =1{Titel gelöscht} other{Titel gelöscht}}", "@snackbarDeletedTracks": { "description": "Snackbar - tracks deleted", "placeholders": { @@ -969,7 +969,7 @@ "@errorUrlFetchFailed": { "description": "Error message - generic URL fetch failure" }, - "errorMissingExtensionSource": "Kann {item} nicht lade wegen fehlender Erweiterungsquelle", + "errorMissingExtensionSource": "Kann {item} nicht laden wegen fehlender Erweiterungsquelle", "@errorMissingExtensionSource": { "description": "Error - extension source not available", "placeholders": { @@ -1287,7 +1287,7 @@ "@logClearLogsTitle": { "description": "Clear logs dialog title" }, - "logClearLogsMessage": "Bist du dir sicher, dass Sie alle Protokolle löschen möchtest?", + "logClearLogsMessage": "Bist du sicher, dass du alle Protokolle löschen möchtest?", "@logClearLogsMessage": { "description": "Clear logs confirmation message" }, @@ -2462,7 +2462,7 @@ "@libraryClearConfirmTitle": { "description": "Dialog title for clear confirmation" }, - "libraryClearConfirmMessage": "Dadurch werden alle gescannten Titel aus Ihrer Bibliothek entfernt. Ihre eigentlichen Musikdateien werden nicht gelöscht.", + "libraryClearConfirmMessage": "Dadurch werden alle gescannten Titel aus deiner Bibliothek entfernt. Deine eigentlichen Musikdateien werden nicht gelöscht.", "@libraryClearConfirmMessage": { "description": "Dialog message for clear confirmation" }, @@ -4509,5 +4509,77 @@ "notifUpdateFailedBody": "Could not download update. Try again later.", "@notifUpdateFailedBody": { "description": "Notification body when app update download fails" + }, + "settingsFiles": "Files & Folders", + "@settingsFiles": { + "description": "Settings menu item - file and folder settings" + }, + "settingsFilesSubtitle": "Download location, filename, folder structure", + "@settingsFilesSubtitle": { + "description": "Subtitle for files & folders settings" + }, + "settingsMetadata": "Metadata", + "@settingsMetadata": { + "description": "Settings menu item - metadata settings" + }, + "settingsMetadataSubtitle": "Cover art, tags, ReplayGain, providers", + "@settingsMetadataSubtitle": { + "description": "Subtitle for metadata settings" + }, + "settingsLyrics": "Lyrics", + "@settingsLyrics": { + "description": "Settings menu item - lyrics settings" + }, + "settingsLyricsSubtitle": "Embed, mode, providers, language options", + "@settingsLyricsSubtitle": { + "description": "Subtitle for lyrics settings" + }, + "settingsApp": "App", + "@settingsApp": { + "description": "Settings menu item - app settings" + }, + "settingsAppSubtitle": "Updates, data, extension repo, debug", + "@settingsAppSubtitle": { + "description": "Subtitle for app settings" + }, + "sectionMetadataProviders": "Providers", + "@sectionMetadataProviders": { + "description": "Settings section header for metadata providers" + }, + "sectionDuplicates": "Duplicates", + "@sectionDuplicates": { + "description": "Settings section header for deduplication" + }, + "sectionLyricsProviderOptions": "Provider Options", + "@sectionLyricsProviderOptions": { + "description": "Settings section header for per-provider lyrics options" + }, + "metadataProvidersTitle": "Metadata Provider Priority", + "@metadataProvidersTitle": { + "description": "Settings item title for metadata provider order" + }, + "metadataProvidersSubtitle": "Drag to set search and metadata source order", + "@metadataProvidersSubtitle": { + "description": "Subtitle for metadata provider priority item" + }, + "downloadDeduplication": "Skip Duplicate Downloads", + "@downloadDeduplication": { + "description": "Setting - skip tracks already in download history" + }, + "downloadDeduplicationEnabled": "Already-downloaded tracks will be skipped", + "@downloadDeduplicationEnabled": { + "description": "Subtitle when deduplication is on" + }, + "downloadDeduplicationDisabled": "All tracks will be downloaded regardless of history", + "@downloadDeduplicationDisabled": { + "description": "Subtitle when deduplication is off" + }, + "downloadFallbackExtensions": "Fallback Extensions", + "@downloadFallbackExtensions": { + "description": "Settings item for configuring fallback extension providers" + }, + "downloadFallbackExtensionsSubtitle": "Choose which extensions can be used as fallback", + "@downloadFallbackExtensionsSubtitle": { + "description": "Subtitle for fallback extensions item" } } \ No newline at end of file diff --git a/lib/l10n/arb/app_es.arb b/lib/l10n/arb/app_es.arb index 248b1478..a17f7299 100644 --- a/lib/l10n/arb/app_es.arb +++ b/lib/l10n/arb/app_es.arb @@ -1728,5 +1728,2858 @@ "type": "int" } } + }, + "navLibrary": "Library", + "@navLibrary": { + "description": "Bottom navigation - Library tab" + }, + "historySearchHint": "Search history...", + "@historySearchHint": { + "description": "Search bar placeholder in history" + }, + "downloadSingleFilenameFormat": "Single Filename Format", + "@downloadSingleFilenameFormat": { + "description": "Setting for output filename pattern for singles/EPs" + }, + "downloadSingleFilenameFormatDescription": "Filename pattern for singles and EPs. Uses the same tags as the album format.", + "@downloadSingleFilenameFormatDescription": { + "description": "Subtitle description for single filename format setting" + }, + "optionsDefaultSearchTab": "Default Search Tab", + "@optionsDefaultSearchTab": { + "description": "Title for the preferred default search tab setting" + }, + "optionsDefaultSearchTabSubtitle": "Choose which tab opens first for new search results.", + "@optionsDefaultSearchTabSubtitle": { + "description": "Subtitle for the preferred default search tab setting" + }, + "optionsReplayGain": "ReplayGain", + "@optionsReplayGain": { + "description": "Title for ReplayGain setting toggle" + }, + "optionsReplayGainSubtitleOn": "Scan loudness and embed ReplayGain tags (EBU R128)", + "@optionsReplayGainSubtitleOn": { + "description": "Subtitle when ReplayGain is enabled" + }, + "optionsReplayGainSubtitleOff": "Disabled: no loudness normalization tags", + "@optionsReplayGainSubtitleOff": { + "description": "Subtitle when ReplayGain is disabled" + }, + "optionsArtistTagMode": "Artist Tag Mode", + "@optionsArtistTagMode": { + "description": "Setting title for how artist metadata is written into files" + }, + "optionsArtistTagModeDescription": "Choose how multiple artists are written into embedded tags.", + "@optionsArtistTagModeDescription": { + "description": "Bottom-sheet description for artist tag mode setting" + }, + "optionsArtistTagModeJoined": "Single joined value", + "@optionsArtistTagModeJoined": { + "description": "Artist tag mode option that joins multiple artists into one value" + }, + "optionsArtistTagModeJoinedSubtitle": "Write one ARTIST value like \"Artist A, Artist B\" for maximum player compatibility.", + "@optionsArtistTagModeJoinedSubtitle": { + "description": "Subtitle for joined artist tag mode" + }, + "optionsArtistTagModeSplitVorbis": "Split tags for FLAC/Opus", + "@optionsArtistTagModeSplitVorbis": { + "description": "Artist tag mode option that writes repeated ARTIST tags for Vorbis formats" + }, + "optionsArtistTagModeSplitVorbisSubtitle": "Write one artist tag per artist for FLAC and Opus; MP3 and M4A stay joined.", + "@optionsArtistTagModeSplitVorbisSubtitle": { + "description": "Subtitle for split Vorbis artist tag mode" + }, + "optionsSpotifyDeprecationWarning": "Spotify search will be deprecated on March 3, 2026 due to Spotify API changes. Please switch to Deezer.", + "@optionsSpotifyDeprecationWarning": { + "description": "Warning about Spotify API deprecation" + }, + "aboutTranslators": "Translators", + "@aboutTranslators": { + "description": "Section for translators" + }, + "aboutTelegramChannel": "Telegram Channel", + "@aboutTelegramChannel": { + "description": "Link to Telegram channel" + }, + "aboutTelegramChannelSubtitle": "Announcements and updates", + "@aboutTelegramChannelSubtitle": { + "description": "Subtitle for Telegram channel" + }, + "aboutTelegramChat": "Telegram Community", + "@aboutTelegramChat": { + "description": "Link to Telegram chat group" + }, + "aboutTelegramChatSubtitle": "Chat with other users", + "@aboutTelegramChatSubtitle": { + "description": "Subtitle for Telegram chat" + }, + "aboutSocial": "Social", + "@aboutSocial": { + "description": "Section for social links" + }, + "aboutSjdonadoDesc": "Creator of I Don't Have Spotify (IDHS). The fallback link resolver that saves the day!", + "@aboutSjdonadoDesc": { + "description": "Credit description for sjdonado" + }, + "aboutSpotiSaver": "SpotiSaver", + "@aboutSpotiSaver": { + "description": "Name of SpotiSaver API service - DO NOT TRANSLATE" + }, + "aboutSpotiSaverDesc": "Tidal Hi-Res FLAC streaming endpoints. A key piece of the lossless puzzle!", + "@aboutSpotiSaverDesc": { + "description": "Credit for SpotiSaver API" + }, + "artistPopular": "Popular", + "@artistPopular": { + "description": "Section header for popular/top tracks" + }, + "artistMonthlyListeners": "{count} monthly listeners", + "@artistMonthlyListeners": { + "description": "Monthly listener count display", + "placeholders": { + "count": { + "type": "String", + "description": "Formatted listener count" + } + } + }, + "setupIcloudNotSupported": "iCloud Drive is not supported. Please use the app Documents folder.", + "@setupIcloudNotSupported": { + "description": "Error when user selects iCloud Drive on iOS" + }, + "dialogDownload": "Download", + "@dialogDownload": { + "description": "Confirm button in Download All dialog" + }, + "csvImportTracks": "{count} tracks from CSV", + "@csvImportTracks": { + "description": "Label shown in quality picker for CSV import", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "snackbarAlreadyInLibrary": "\"{trackName}\" already exists in your library", + "@snackbarAlreadyInLibrary": { + "description": "Snackbar - track already exists in local library", + "placeholders": { + "trackName": { + "type": "String" + } + } + }, + "errorUrlNotRecognized": "Link not recognized", + "@errorUrlNotRecognized": { + "description": "Error title - URL not handled by any extension or service" + }, + "errorUrlNotRecognizedMessage": "This link is not supported. Make sure the URL is correct and a compatible extension is installed.", + "@errorUrlNotRecognizedMessage": { + "description": "Error message - URL not recognized explanation" + }, + "errorUrlFetchFailed": "Failed to load content from this link. Please try again.", + "@errorUrlFetchFailed": { + "description": "Error message - generic URL fetch failure" + }, + "searchSortTitle": "Sort Results", + "@searchSortTitle": { + "description": "Bottom sheet title for search sort options" + }, + "searchSortDefault": "Default", + "@searchSortDefault": { + "description": "Sort option - default API order" + }, + "searchSortTitleAZ": "Title (A-Z)", + "@searchSortTitleAZ": { + "description": "Sort option - title ascending" + }, + "searchSortTitleZA": "Title (Z-A)", + "@searchSortTitleZA": { + "description": "Sort option - title descending" + }, + "searchSortArtistAZ": "Artist (A-Z)", + "@searchSortArtistAZ": { + "description": "Sort option - artist ascending" + }, + "searchSortArtistZA": "Artist (Z-A)", + "@searchSortArtistZA": { + "description": "Sort option - artist descending" + }, + "searchSortDurationShort": "Duration (Shortest)", + "@searchSortDurationShort": { + "description": "Sort option - shortest duration first" + }, + "searchSortDurationLong": "Duration (Longest)", + "@searchSortDurationLong": { + "description": "Sort option - longest duration first" + }, + "searchSortDateOldest": "Release Date (Oldest)", + "@searchSortDateOldest": { + "description": "Sort option - oldest release first" + }, + "searchSortDateNewest": "Release Date (Newest)", + "@searchSortDateNewest": { + "description": "Sort option - newest release first" + }, + "filenameShowAdvancedTags": "Show advanced tags", + "@filenameShowAdvancedTags": { + "description": "Toggle label for showing advanced filename tags" + }, + "filenameShowAdvancedTagsDescription": "Enable formatted tags for track padding and date patterns", + "@filenameShowAdvancedTagsDescription": { + "description": "Description for advanced filename tag toggle" + }, + "folderOrganizationByPlaylist": "By Playlist", + "@folderOrganizationByPlaylist": { + "description": "Folder option - playlist folders" + }, + "folderOrganizationByPlaylistSubtitle": "Separate folder for each playlist", + "@folderOrganizationByPlaylistSubtitle": { + "description": "Subtitle for playlist folder option" + }, + "providerPriorityFallbackExtensionsTitle": "Extension Fallback", + "@providerPriorityFallbackExtensionsTitle": { + "description": "Section title for choosing which download extensions can be used as fallback providers" + }, + "providerPriorityFallbackExtensionsDescription": "Choose which installed download extensions can be used during automatic fallback. Built-in providers still follow the priority order above.", + "@providerPriorityFallbackExtensionsDescription": { + "description": "Section description for extension fallback selection" + }, + "providerPriorityFallbackExtensionsHint": "Only enabled extensions with download-provider capability are listed here.", + "@providerPriorityFallbackExtensionsHint": { + "description": "Hint below the extension fallback selection list" + }, + "sectionLyrics": "Lyrics", + "@sectionLyrics": { + "description": "Settings section header" + }, + "lyricsMode": "Lyrics Mode", + "@lyricsMode": { + "description": "Setting - how to save lyrics" + }, + "lyricsModeDescription": "Choose how lyrics are saved with your downloads", + "@lyricsModeDescription": { + "description": "Lyrics mode picker description" + }, + "lyricsModeEmbed": "Embed in file", + "@lyricsModeEmbed": { + "description": "Lyrics mode option - embed in audio file" + }, + "lyricsModeEmbedSubtitle": "Lyrics stored inside FLAC metadata", + "@lyricsModeEmbedSubtitle": { + "description": "Subtitle for embed option" + }, + "lyricsModeExternal": "External .lrc file", + "@lyricsModeExternal": { + "description": "Lyrics mode option - separate LRC file" + }, + "lyricsModeExternalSubtitle": "Separate .lrc file for players like Samsung Music", + "@lyricsModeExternalSubtitle": { + "description": "Subtitle for external option" + }, + "lyricsModeBoth": "Both", + "@lyricsModeBoth": { + "description": "Lyrics mode option - embed and external" + }, + "lyricsModeBothSubtitle": "Embed and save .lrc file", + "@lyricsModeBothSubtitle": { + "description": "Subtitle for both option" + }, + "trackGenre": "Genre", + "@trackGenre": { + "description": "Metadata label - music genre" + }, + "trackLabel": "Label", + "@trackLabel": { + "description": "Metadata label - record label" + }, + "trackCopyright": "Copyright", + "@trackCopyright": { + "description": "Metadata label - copyright information" + }, + "trackLyricsNotInFile": "No lyrics found in this file", + "@trackLyricsNotInFile": { + "description": "Message when no embedded lyrics in audio file" + }, + "trackFetchOnlineLyrics": "Fetch from Online", + "@trackFetchOnlineLyrics": { + "description": "Action - fetch lyrics from online providers" + }, + "trackEmbedLyrics": "Embed Lyrics", + "@trackEmbedLyrics": { + "description": "Action - embed lyrics into audio file" + }, + "trackLyricsEmbedded": "Lyrics embedded successfully", + "@trackLyricsEmbedded": { + "description": "Snackbar - lyrics saved to file" + }, + "trackInstrumental": "Instrumental track", + "@trackInstrumental": { + "description": "Message when track is instrumental (no lyrics)" + }, + "storeAddRepoTitle": "Add Extension Repository", + "@storeAddRepoTitle": { + "description": "Store setup screen - heading when no repo is configured" + }, + "storeAddRepoDescription": "Enter a GitHub repository URL that contains a registry.json file to browse and install extensions.", + "@storeAddRepoDescription": { + "description": "Store setup screen - explanatory text" + }, + "storeRepoUrlLabel": "Repository URL", + "@storeRepoUrlLabel": { + "description": "Label for the repository URL input field" + }, + "storeRepoUrlHint": "https://github.com/user/repo", + "@storeRepoUrlHint": { + "description": "Hint/placeholder for the repository URL input field" + }, + "storeRepoUrlHelper": "e.g. https://github.com/user/extensions-repo", + "@storeRepoUrlHelper": { + "description": "Helper text below the repository URL input field" + }, + "storeAddRepoButton": "Add Repository", + "@storeAddRepoButton": { + "description": "Button to submit a new repository URL" + }, + "storeChangeRepoTooltip": "Change repository", + "@storeChangeRepoTooltip": { + "description": "Tooltip for the change-repository icon button in the app bar" + }, + "storeRepoDialogTitle": "Extension Repository", + "@storeRepoDialogTitle": { + "description": "Title of the change/remove repository dialog" + }, + "storeRepoDialogCurrent": "Current repository:", + "@storeRepoDialogCurrent": { + "description": "Label shown above the current repository URL in the dialog" + }, + "storeNewRepoUrlLabel": "New Repository URL", + "@storeNewRepoUrlLabel": { + "description": "Label for the new repository URL field inside the dialog" + }, + "storeLoadError": "Failed to load repository", + "@storeLoadError": { + "description": "Error heading when the store cannot be loaded" + }, + "storeEmptyNoExtensions": "No extensions available", + "@storeEmptyNoExtensions": { + "description": "Message when store has no extensions" + }, + "storeEmptyNoResults": "No extensions found", + "@storeEmptyNoResults": { + "description": "Message when search/filter returns no results" + }, + "extensionsFallbackTitle": "Fallback Extensions", + "@extensionsFallbackTitle": { + "description": "Setting and page title for choosing which download extensions can be used during fallback" + }, + "extensionsFallbackSubtitle": "Choose which installed download extensions can be used as fallback", + "@extensionsFallbackSubtitle": { + "description": "Subtitle for download fallback extensions menu" + }, + "downloadLossy320": "Lossy 320kbps", + "@downloadLossy320": { + "description": "Quality option label for Tidal lossy 320kbps" + }, + "downloadLossyFormat": "Lossy Format", + "@downloadLossyFormat": { + "description": "Setting title to pick output format for Tidal lossy downloads" + }, + "downloadLossy320Format": "Lossy 320kbps Format", + "@downloadLossy320Format": { + "description": "Title of the Tidal lossy format picker bottom sheet" + }, + "downloadLossy320FormatDesc": "Choose the output format for Tidal 320kbps lossy downloads. The original AAC stream will be converted to your selected format.", + "@downloadLossy320FormatDesc": { + "description": "Description in the Tidal lossy format picker" + }, + "downloadLossyMp3": "MP3 320kbps", + "@downloadLossyMp3": { + "description": "Tidal lossy format option - MP3 320kbps" + }, + "downloadLossyMp3Subtitle": "Best compatibility, ~10MB per track", + "@downloadLossyMp3Subtitle": { + "description": "Subtitle for MP3 320kbps Tidal lossy option" + }, + "downloadLossyOpus256": "Opus 256kbps", + "@downloadLossyOpus256": { + "description": "Tidal lossy format option - Opus 256kbps" + }, + "downloadLossyOpus256Subtitle": "Best quality Opus, ~8MB per track", + "@downloadLossyOpus256Subtitle": { + "description": "Subtitle for Opus 256kbps Tidal lossy option" + }, + "downloadLossyOpus128": "Opus 128kbps", + "@downloadLossyOpus128": { + "description": "Tidal lossy format option - Opus 128kbps" + }, + "downloadLossyOpus128Subtitle": "Smallest size, ~4MB per track", + "@downloadLossyOpus128Subtitle": { + "description": "Subtitle for Opus 128kbps Tidal lossy option" + }, + "downloadUseAlbumArtistForFolders": "Use Album Artist for folders", + "@downloadUseAlbumArtistForFolders": { + "description": "Setting - choose whether artist folders use Album Artist or Track Artist" + }, + "downloadUsePrimaryArtistOnly": "Primary artist only for folders", + "@downloadUsePrimaryArtistOnly": { + "description": "Setting - strip featured artists from folder name" + }, + "downloadUsePrimaryArtistOnlyEnabled": "Featured artists removed from folder name (e.g. Justin Bieber, Quavo → Justin Bieber)", + "@downloadUsePrimaryArtistOnlyEnabled": { + "description": "Subtitle when primary artist only is enabled" + }, + "downloadUsePrimaryArtistOnlyDisabled": "Full artist string used for folder name", + "@downloadUsePrimaryArtistOnlyDisabled": { + "description": "Subtitle when primary artist only is disabled" + }, + "settingsAutoExportFailed": "Auto-export failed downloads", + "@settingsAutoExportFailed": { + "description": "Setting toggle for auto-export" + }, + "settingsAutoExportFailedSubtitle": "Save failed downloads to TXT file automatically", + "@settingsAutoExportFailedSubtitle": { + "description": "Subtitle for auto-export setting" + }, + "settingsDownloadNetwork": "Download Network", + "@settingsDownloadNetwork": { + "description": "Setting for network type preference" + }, + "settingsDownloadNetworkAny": "WiFi + Mobile Data", + "@settingsDownloadNetworkAny": { + "description": "Network option - use any connection" + }, + "settingsDownloadNetworkWifiOnly": "WiFi Only", + "@settingsDownloadNetworkWifiOnly": { + "description": "Network option - only use WiFi" + }, + "settingsDownloadNetworkSubtitle": "Choose which network to use for downloads. When set to WiFi Only, downloads will pause on mobile data.", + "@settingsDownloadNetworkSubtitle": { + "description": "Subtitle explaining network preference" + }, + "albumFolderArtistAlbumSingles": "Artist / Album + Singles", + "@albumFolderArtistAlbumSingles": { + "description": "Album folder option with singles inside artist" + }, + "albumFolderArtistAlbumSinglesSubtitle": "Artist/Album/ and Artist/Singles/", + "@albumFolderArtistAlbumSinglesSubtitle": { + "description": "Folder structure example" + }, + "albumFolderArtistAlbumFlat": "Artist / Album (Singles flat)", + "@albumFolderArtistAlbumFlat": { + "description": "Album folder option with singles directly in artist folder" + }, + "albumFolderArtistAlbumFlatSubtitle": "Artist/Album/ and Artist/song.flac", + "@albumFolderArtistAlbumFlatSubtitle": { + "description": "Folder structure example for flat singles" + }, + "downloadedAlbumDiscHeader": "Disc {discNumber}", + "@downloadedAlbumDiscHeader": { + "description": "Header for disc separator in multi-disc albums", + "placeholders": { + "discNumber": { + "type": "int", + "example": "1" + } + } + }, + "recentTypeArtist": "Artist", + "@recentTypeArtist": { + "description": "Recent access item type - artist" + }, + "recentTypeAlbum": "Album", + "@recentTypeAlbum": { + "description": "Recent access item type - album" + }, + "recentTypeSong": "Song", + "@recentTypeSong": { + "description": "Recent access item type - song/track" + }, + "recentTypePlaylist": "Playlist", + "@recentTypePlaylist": { + "description": "Recent access item type - playlist" + }, + "recentEmpty": "No recent items yet", + "@recentEmpty": { + "description": "Empty state text for recent access list" + }, + "recentShowAllDownloads": "Show All Downloads", + "@recentShowAllDownloads": { + "description": "Button label to unhide hidden downloads in recent access" + }, + "recentPlaylistInfo": "Playlist: {name}", + "@recentPlaylistInfo": { + "description": "Snackbar message when tapping playlist in recent access", + "placeholders": { + "name": { + "type": "String", + "description": "Playlist name" + } + } + }, + "discographyDownload": "Download Discography", + "@discographyDownload": { + "description": "Button - download artist discography" + }, + "discographyDownloadAll": "Download All", + "@discographyDownloadAll": { + "description": "Option - download entire discography" + }, + "discographyDownloadAllSubtitle": "{count} tracks from {albumCount} releases", + "@discographyDownloadAllSubtitle": { + "description": "Subtitle showing total tracks and albums", + "placeholders": { + "count": { + "type": "int" + }, + "albumCount": { + "type": "int" + } + } + }, + "discographyAlbumsOnly": "Albums Only", + "@discographyAlbumsOnly": { + "description": "Option - download only albums" + }, + "discographyAlbumsOnlySubtitle": "{count} tracks from {albumCount} albums", + "@discographyAlbumsOnlySubtitle": { + "description": "Subtitle showing album tracks count", + "placeholders": { + "count": { + "type": "int" + }, + "albumCount": { + "type": "int" + } + } + }, + "discographySinglesOnly": "Singles & EPs Only", + "@discographySinglesOnly": { + "description": "Option - download only singles" + }, + "discographySinglesOnlySubtitle": "{count} tracks from {albumCount} singles", + "@discographySinglesOnlySubtitle": { + "description": "Subtitle showing singles tracks count", + "placeholders": { + "count": { + "type": "int" + }, + "albumCount": { + "type": "int" + } + } + }, + "discographySelectAlbums": "Select Albums...", + "@discographySelectAlbums": { + "description": "Option - manually select albums to download" + }, + "discographySelectAlbumsSubtitle": "Choose specific albums or singles", + "@discographySelectAlbumsSubtitle": { + "description": "Subtitle for select albums option" + }, + "discographyFetchingTracks": "Fetching tracks...", + "@discographyFetchingTracks": { + "description": "Progress - fetching album tracks" + }, + "discographyFetchingAlbum": "Fetching {current} of {total}...", + "@discographyFetchingAlbum": { + "description": "Progress - fetching specific album", + "placeholders": { + "current": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "discographySelectedCount": "{count} selected", + "@discographySelectedCount": { + "description": "Selection count badge", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "discographyDownloadSelected": "Download Selected", + "@discographyDownloadSelected": { + "description": "Button - download selected albums" + }, + "discographyAddedToQueue": "Added {count} tracks to queue", + "@discographyAddedToQueue": { + "description": "Snackbar - tracks added from discography", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "discographySkippedDownloaded": "{added} added, {skipped} already downloaded", + "@discographySkippedDownloaded": { + "description": "Snackbar - with skipped tracks count", + "placeholders": { + "added": { + "type": "int" + }, + "skipped": { + "type": "int" + } + } + }, + "discographyNoAlbums": "No albums available", + "@discographyNoAlbums": { + "description": "Error - no albums found for artist" + }, + "discographyFailedToFetch": "Failed to fetch some albums", + "@discographyFailedToFetch": { + "description": "Error - some albums failed to load" + }, + "sectionStorageAccess": "Storage Access", + "@sectionStorageAccess": { + "description": "Section header for storage access settings" + }, + "allFilesAccess": "All Files Access", + "@allFilesAccess": { + "description": "Toggle for MANAGE_EXTERNAL_STORAGE permission" + }, + "allFilesAccessEnabledSubtitle": "Can write to any folder", + "@allFilesAccessEnabledSubtitle": { + "description": "Subtitle when all files access is enabled" + }, + "allFilesAccessDisabledSubtitle": "Limited to media folders only", + "@allFilesAccessDisabledSubtitle": { + "description": "Subtitle when all files access is disabled" + }, + "allFilesAccessDescription": "Enable this if you encounter write errors when saving to custom folders. Android 13+ restricts access to certain directories by default.", + "@allFilesAccessDescription": { + "description": "Description explaining when to enable all files access" + }, + "allFilesAccessDeniedMessage": "Permission was denied. Please enable 'All files access' manually in system settings.", + "@allFilesAccessDeniedMessage": { + "description": "Message when permission is permanently denied" + }, + "allFilesAccessDisabledMessage": "All Files Access disabled. The app will use limited storage access.", + "@allFilesAccessDisabledMessage": { + "description": "Snackbar message when user disables all files access" + }, + "settingsLocalLibrary": "Local Library", + "@settingsLocalLibrary": { + "description": "Settings menu item - local library" + }, + "settingsLocalLibrarySubtitle": "Scan music & detect duplicates", + "@settingsLocalLibrarySubtitle": { + "description": "Subtitle for local library settings" + }, + "settingsCache": "Storage & Cache", + "@settingsCache": { + "description": "Settings menu item - cache management" + }, + "settingsCacheSubtitle": "View size and clear cached data", + "@settingsCacheSubtitle": { + "description": "Subtitle for cache management menu" + }, + "libraryTitle": "Local Library", + "@libraryTitle": { + "description": "Library settings page title" + }, + "libraryScanSettings": "Scan Settings", + "@libraryScanSettings": { + "description": "Section header for scan settings" + }, + "libraryEnableLocalLibrary": "Enable Local Library", + "@libraryEnableLocalLibrary": { + "description": "Toggle to enable library scanning" + }, + "libraryEnableLocalLibrarySubtitle": "Scan and track your existing music", + "@libraryEnableLocalLibrarySubtitle": { + "description": "Subtitle for enable toggle" + }, + "libraryFolder": "Library Folder", + "@libraryFolder": { + "description": "Folder selection setting" + }, + "libraryFolderHint": "Tap to select folder", + "@libraryFolderHint": { + "description": "Placeholder when no folder selected" + }, + "libraryShowDuplicateIndicator": "Show Duplicate Indicator", + "@libraryShowDuplicateIndicator": { + "description": "Toggle for duplicate indicator in search" + }, + "libraryShowDuplicateIndicatorSubtitle": "Show when searching for existing tracks", + "@libraryShowDuplicateIndicatorSubtitle": { + "description": "Subtitle for duplicate indicator toggle" + }, + "libraryAutoScan": "Auto Scan", + "@libraryAutoScan": { + "description": "Setting for automatic library scanning" + }, + "libraryAutoScanSubtitle": "Automatically scan your library for new files", + "@libraryAutoScanSubtitle": { + "description": "Subtitle for auto scan setting" + }, + "libraryAutoScanOff": "Off", + "@libraryAutoScanOff": { + "description": "Auto scan disabled" + }, + "libraryAutoScanOnOpen": "Every app open", + "@libraryAutoScanOnOpen": { + "description": "Auto scan when app opens" + }, + "libraryAutoScanDaily": "Daily", + "@libraryAutoScanDaily": { + "description": "Auto scan once per day" + }, + "libraryAutoScanWeekly": "Weekly", + "@libraryAutoScanWeekly": { + "description": "Auto scan once per week" + }, + "libraryActions": "Actions", + "@libraryActions": { + "description": "Section header for library actions" + }, + "libraryScan": "Scan Library", + "@libraryScan": { + "description": "Button to start library scan" + }, + "libraryScanSubtitle": "Scan for audio files", + "@libraryScanSubtitle": { + "description": "Subtitle for scan button" + }, + "libraryScanSelectFolderFirst": "Select a folder first", + "@libraryScanSelectFolderFirst": { + "description": "Message when trying to scan without folder" + }, + "libraryCleanupMissingFiles": "Cleanup Missing Files", + "@libraryCleanupMissingFiles": { + "description": "Button to remove entries for missing files" + }, + "libraryCleanupMissingFilesSubtitle": "Remove entries for files that no longer exist", + "@libraryCleanupMissingFilesSubtitle": { + "description": "Subtitle for cleanup button" + }, + "libraryClear": "Clear Library", + "@libraryClear": { + "description": "Button to clear all library entries" + }, + "libraryClearSubtitle": "Remove all scanned tracks", + "@libraryClearSubtitle": { + "description": "Subtitle for clear button" + }, + "libraryClearConfirmTitle": "Clear Library", + "@libraryClearConfirmTitle": { + "description": "Dialog title for clear confirmation" + }, + "libraryClearConfirmMessage": "This will remove all scanned tracks from your library. Your actual music files will not be deleted.", + "@libraryClearConfirmMessage": { + "description": "Dialog message for clear confirmation" + }, + "libraryAbout": "About Local Library", + "@libraryAbout": { + "description": "Section header for about info" + }, + "libraryAboutDescription": "Scans your existing music collection to detect duplicates when downloading. Supports FLAC, M4A, MP3, Opus, and OGG formats. Metadata is read from file tags when available.", + "@libraryAboutDescription": { + "description": "Description of local library feature" + }, + "libraryTracksUnit": "{count, plural, =1{track} other{tracks}}", + "@libraryTracksUnit": { + "description": "Unit label for tracks count (without the number itself)", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "libraryFilesUnit": "{count, plural, =1{file} other{files}}", + "@libraryFilesUnit": { + "description": "Unit label for files count during library scanning", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "libraryLastScanned": "Last scanned: {time}", + "@libraryLastScanned": { + "description": "Last scan time display", + "placeholders": { + "time": { + "type": "String" + } + } + }, + "libraryLastScannedNever": "Never", + "@libraryLastScannedNever": { + "description": "Shown when library has never been scanned" + }, + "libraryScanning": "Scanning...", + "@libraryScanning": { + "description": "Status during scan" + }, + "libraryScanFinalizing": "Finalizing library...", + "@libraryScanFinalizing": { + "description": "Status shown after file scanning finishes but library persistence is still running" + }, + "libraryScanProgress": "{progress}% of {total} files", + "@libraryScanProgress": { + "description": "Scan progress display", + "placeholders": { + "progress": { + "type": "String" + }, + "total": { + "type": "int" + } + } + }, + "libraryInLibrary": "In Library", + "@libraryInLibrary": { + "description": "Badge shown on tracks that exist in local library" + }, + "libraryRemovedMissingFiles": "Removed {count} missing files from library", + "@libraryRemovedMissingFiles": { + "description": "Snackbar after cleanup", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "libraryCleared": "Library cleared", + "@libraryCleared": { + "description": "Snackbar after clearing library" + }, + "libraryStorageAccessRequired": "Storage Access Required", + "@libraryStorageAccessRequired": { + "description": "Dialog title for storage permission" + }, + "libraryStorageAccessMessage": "SpotiFLAC needs storage access to scan your music library. Please grant permission in settings.", + "@libraryStorageAccessMessage": { + "description": "Dialog message for storage permission" + }, + "libraryFolderNotExist": "Selected folder does not exist", + "@libraryFolderNotExist": { + "description": "Error when folder doesn't exist" + }, + "librarySourceDownloaded": "Downloaded", + "@librarySourceDownloaded": { + "description": "Badge for tracks downloaded via SpotiFLAC" + }, + "librarySourceLocal": "Local", + "@librarySourceLocal": { + "description": "Badge for tracks from local library scan" + }, + "libraryFilterAll": "All", + "@libraryFilterAll": { + "description": "Filter chip - show all library items" + }, + "libraryFilterDownloaded": "Downloaded", + "@libraryFilterDownloaded": { + "description": "Filter chip - show only downloaded items" + }, + "libraryFilterLocal": "Local", + "@libraryFilterLocal": { + "description": "Filter chip - show only local library items" + }, + "libraryFilterTitle": "Filters", + "@libraryFilterTitle": { + "description": "Filter bottom sheet title" + }, + "libraryFilterReset": "Reset", + "@libraryFilterReset": { + "description": "Reset all filters button" + }, + "libraryFilterApply": "Apply", + "@libraryFilterApply": { + "description": "Apply filters button" + }, + "libraryFilterSource": "Source", + "@libraryFilterSource": { + "description": "Filter section - source type" + }, + "libraryFilterQuality": "Quality", + "@libraryFilterQuality": { + "description": "Filter section - audio quality" + }, + "libraryFilterQualityHiRes": "Hi-Res (24bit)", + "@libraryFilterQualityHiRes": { + "description": "Filter option - high resolution audio" + }, + "libraryFilterQualityCD": "CD (16bit)", + "@libraryFilterQualityCD": { + "description": "Filter option - CD quality audio" + }, + "libraryFilterQualityLossy": "Lossy", + "@libraryFilterQualityLossy": { + "description": "Filter option - lossy compressed audio" + }, + "libraryFilterFormat": "Format", + "@libraryFilterFormat": { + "description": "Filter section - file format" + }, + "libraryFilterMetadata": "Metadata", + "@libraryFilterMetadata": { + "description": "Filter section - metadata completeness" + }, + "libraryFilterMetadataComplete": "Complete metadata", + "@libraryFilterMetadataComplete": { + "description": "Filter option - items with complete metadata" + }, + "libraryFilterMetadataMissingAny": "Missing any metadata", + "@libraryFilterMetadataMissingAny": { + "description": "Filter option - items missing any tracked metadata field" + }, + "libraryFilterMetadataMissingYear": "Missing year", + "@libraryFilterMetadataMissingYear": { + "description": "Filter option - items missing release year/date" + }, + "libraryFilterMetadataMissingGenre": "Missing genre", + "@libraryFilterMetadataMissingGenre": { + "description": "Filter option - items missing genre" + }, + "libraryFilterMetadataMissingAlbumArtist": "Missing album artist", + "@libraryFilterMetadataMissingAlbumArtist": { + "description": "Filter option - items missing album artist" + }, + "libraryFilterSort": "Sort", + "@libraryFilterSort": { + "description": "Filter section - sort order" + }, + "libraryFilterSortLatest": "Latest", + "@libraryFilterSortLatest": { + "description": "Sort option - newest first" + }, + "libraryFilterSortOldest": "Oldest", + "@libraryFilterSortOldest": { + "description": "Sort option - oldest first" + }, + "libraryFilterSortAlbumAsc": "Album (A-Z)", + "@libraryFilterSortAlbumAsc": { + "description": "Sort option - album ascending" + }, + "libraryFilterSortAlbumDesc": "Album (Z-A)", + "@libraryFilterSortAlbumDesc": { + "description": "Sort option - album descending" + }, + "libraryFilterSortGenreAsc": "Genre (A-Z)", + "@libraryFilterSortGenreAsc": { + "description": "Sort option - genre ascending" + }, + "libraryFilterSortGenreDesc": "Genre (Z-A)", + "@libraryFilterSortGenreDesc": { + "description": "Sort option - genre descending" + }, + "timeJustNow": "Just now", + "@timeJustNow": { + "description": "Relative time - less than a minute ago" + }, + "timeMinutesAgo": "{count, plural, =1{1 minute ago} other{{count} minutes ago}}", + "@timeMinutesAgo": { + "description": "Relative time - minutes ago", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "timeHoursAgo": "{count, plural, =1{1 hour ago} other{{count} hours ago}}", + "@timeHoursAgo": { + "description": "Relative time - hours ago", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "tutorialWelcomeTitle": "Welcome to SpotiFLAC!", + "@tutorialWelcomeTitle": { + "description": "Tutorial welcome page title" + }, + "tutorialWelcomeDesc": "Let's learn how to download your favorite music in lossless quality. This quick tutorial will show you the basics.", + "@tutorialWelcomeDesc": { + "description": "Tutorial welcome page description" + }, + "tutorialWelcomeTip1": "Download music from Spotify, Deezer, or paste any supported URL", + "@tutorialWelcomeTip1": { + "description": "Tutorial welcome tip 1" + }, + "tutorialWelcomeTip2": "Get FLAC quality audio from Tidal, Qobuz, or Deezer", + "@tutorialWelcomeTip2": { + "description": "Tutorial welcome tip 2" + }, + "tutorialWelcomeTip3": "Automatic metadata, cover art, and lyrics embedding", + "@tutorialWelcomeTip3": { + "description": "Tutorial welcome tip 3" + }, + "tutorialSearchTitle": "Finding Music", + "@tutorialSearchTitle": { + "description": "Tutorial search page title" + }, + "tutorialSearchDesc": "There are two easy ways to find music you want to download.", + "@tutorialSearchDesc": { + "description": "Tutorial search page description" + }, + "tutorialDownloadTitle": "Downloading Music", + "@tutorialDownloadTitle": { + "description": "Tutorial download page title" + }, + "tutorialDownloadDesc": "Downloading music is simple and fast. Here's how it works.", + "@tutorialDownloadDesc": { + "description": "Tutorial download page description" + }, + "tutorialLibraryTitle": "Your Library", + "@tutorialLibraryTitle": { + "description": "Tutorial library page title" + }, + "tutorialLibraryDesc": "All your downloaded music is organized in the Library tab.", + "@tutorialLibraryDesc": { + "description": "Tutorial library page description" + }, + "tutorialLibraryTip1": "View download progress and queue in the Library tab", + "@tutorialLibraryTip1": { + "description": "Tutorial library tip 1" + }, + "tutorialLibraryTip2": "Tap any track to play it with your music player", + "@tutorialLibraryTip2": { + "description": "Tutorial library tip 2" + }, + "tutorialLibraryTip3": "Switch between list and grid view for better browsing", + "@tutorialLibraryTip3": { + "description": "Tutorial library tip 3" + }, + "tutorialExtensionsTitle": "Extensions", + "@tutorialExtensionsTitle": { + "description": "Tutorial extensions page title" + }, + "tutorialExtensionsDesc": "Extend the app's capabilities with community extensions.", + "@tutorialExtensionsDesc": { + "description": "Tutorial extensions page description" + }, + "tutorialExtensionsTip1": "Browse the Repo tab to discover useful extensions", + "@tutorialExtensionsTip1": { + "description": "Tutorial extensions tip 1" + }, + "tutorialExtensionsTip2": "Add new download providers or search sources", + "@tutorialExtensionsTip2": { + "description": "Tutorial extensions tip 2" + }, + "tutorialExtensionsTip3": "Get lyrics, enhanced metadata, and more features", + "@tutorialExtensionsTip3": { + "description": "Tutorial extensions tip 3" + }, + "tutorialSettingsTitle": "Customize Your Experience", + "@tutorialSettingsTitle": { + "description": "Tutorial settings page title" + }, + "tutorialSettingsDesc": "Personalize the app in Settings to match your preferences.", + "@tutorialSettingsDesc": { + "description": "Tutorial settings page description" + }, + "tutorialSettingsTip1": "Change download location and folder organization", + "@tutorialSettingsTip1": { + "description": "Tutorial settings tip 1" + }, + "tutorialSettingsTip2": "Set default audio quality and format preferences", + "@tutorialSettingsTip2": { + "description": "Tutorial settings tip 2" + }, + "tutorialSettingsTip3": "Customize app theme and appearance", + "@tutorialSettingsTip3": { + "description": "Tutorial settings tip 3" + }, + "tutorialReadyMessage": "You're all set! Start downloading your favorite music now.", + "@tutorialReadyMessage": { + "description": "Tutorial completion message" + }, + "libraryForceFullScan": "Force Full Scan", + "@libraryForceFullScan": { + "description": "Button to force a complete rescan of library" + }, + "libraryForceFullScanSubtitle": "Rescan all files, ignoring cache", + "@libraryForceFullScanSubtitle": { + "description": "Subtitle for force full scan button" + }, + "cleanupOrphanedDownloads": "Cleanup Orphaned Downloads", + "@cleanupOrphanedDownloads": { + "description": "Button to remove history entries for deleted files" + }, + "cleanupOrphanedDownloadsSubtitle": "Remove history entries for files that no longer exist", + "@cleanupOrphanedDownloadsSubtitle": { + "description": "Subtitle for orphaned cleanup button" + }, + "cleanupOrphanedDownloadsResult": "Removed {count} orphaned entries from history", + "@cleanupOrphanedDownloadsResult": { + "description": "Snackbar after orphan cleanup", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "cleanupOrphanedDownloadsNone": "No orphaned entries found", + "@cleanupOrphanedDownloadsNone": { + "description": "Snackbar when no orphans found" + }, + "cacheTitle": "Storage & Cache", + "@cacheTitle": { + "description": "Cache management page title" + }, + "cacheSummaryTitle": "Cache overview", + "@cacheSummaryTitle": { + "description": "Heading for cache summary card" + }, + "cacheSummarySubtitle": "Clearing cache will not remove downloaded music files.", + "@cacheSummarySubtitle": { + "description": "Helper text for cache summary card" + }, + "cacheEstimatedTotal": "Estimated cache usage: {size}", + "@cacheEstimatedTotal": { + "description": "Total cache size shown in summary", + "placeholders": { + "size": { + "type": "String" + } + } + }, + "cacheSectionStorage": "Cached Data", + "@cacheSectionStorage": { + "description": "Section header for cache entries" + }, + "cacheSectionMaintenance": "Maintenance", + "@cacheSectionMaintenance": { + "description": "Section header for cleanup actions" + }, + "cacheAppDirectory": "App cache directory", + "@cacheAppDirectory": { + "description": "Cache item title for app cache directory" + }, + "cacheAppDirectoryDesc": "HTTP responses, WebView data, and other temporary app data.", + "@cacheAppDirectoryDesc": { + "description": "Description of what app cache directory contains" + }, + "cacheTempDirectory": "Temporary directory", + "@cacheTempDirectory": { + "description": "Cache item title for temporary files directory" + }, + "cacheTempDirectoryDesc": "Temporary files from downloads and audio conversion.", + "@cacheTempDirectoryDesc": { + "description": "Description of what temporary directory contains" + }, + "cacheCoverImage": "Cover image cache", + "@cacheCoverImage": { + "description": "Cache item title for persistent cover images" + }, + "cacheCoverImageDesc": "Downloaded album and track cover art. Will re-download when viewed.", + "@cacheCoverImageDesc": { + "description": "Description of what cover image cache contains" + }, + "cacheLibraryCover": "Library cover cache", + "@cacheLibraryCover": { + "description": "Cache item title for local library cover art images" + }, + "cacheLibraryCoverDesc": "Cover art extracted from local music files. Will re-extract on next scan.", + "@cacheLibraryCoverDesc": { + "description": "Description of what library cover cache contains" + }, + "cacheExploreFeed": "Explore feed cache", + "@cacheExploreFeed": { + "description": "Cache item title for explore home feed cache" + }, + "cacheExploreFeedDesc": "Explore tab content (new releases, trending). Will refresh on next visit.", + "@cacheExploreFeedDesc": { + "description": "Description of what explore feed cache contains" + }, + "cacheTrackLookup": "Track lookup cache", + "@cacheTrackLookup": { + "description": "Cache item title for track ID lookup cache" + }, + "cacheTrackLookupDesc": "Spotify/Deezer track ID lookups. Clearing may slow next few searches.", + "@cacheTrackLookupDesc": { + "description": "Description of what track lookup cache contains" + }, + "cacheCleanupUnusedDesc": "Remove orphaned download history and library entries for missing files.", + "@cacheCleanupUnusedDesc": { + "description": "Description of what cleanup unused data does" + }, + "cacheNoData": "No cached data", + "@cacheNoData": { + "description": "Label when cache category has no data" + }, + "cacheSizeWithFiles": "{size} in {count} files", + "@cacheSizeWithFiles": { + "description": "Cache size and file count", + "placeholders": { + "size": { + "type": "String" + }, + "count": { + "type": "int" + } + } + }, + "cacheSizeOnly": "{size}", + "@cacheSizeOnly": { + "description": "Cache size only", + "placeholders": { + "size": { + "type": "String" + } + } + }, + "cacheEntries": "{count} entries", + "@cacheEntries": { + "description": "Track cache entry count", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "cacheClearSuccess": "Cleared: {target}", + "@cacheClearSuccess": { + "description": "Snackbar after clearing selected cache", + "placeholders": { + "target": { + "type": "String" + } + } + }, + "cacheClearConfirmTitle": "Clear cache?", + "@cacheClearConfirmTitle": { + "description": "Dialog title before clearing one cache category" + }, + "cacheClearConfirmMessage": "This will clear cached data for {target}. Downloaded music files will not be deleted.", + "@cacheClearConfirmMessage": { + "description": "Dialog message before clearing selected cache", + "placeholders": { + "target": { + "type": "String" + } + } + }, + "cacheClearAllConfirmTitle": "Clear all cache?", + "@cacheClearAllConfirmTitle": { + "description": "Dialog title before clearing all caches" + }, + "cacheClearAllConfirmMessage": "This will clear all cache categories on this page. Downloaded music files will not be deleted.", + "@cacheClearAllConfirmMessage": { + "description": "Dialog message before clearing all caches" + }, + "cacheClearAll": "Clear all cache", + "@cacheClearAll": { + "description": "Button label to clear all caches" + }, + "cacheCleanupUnused": "Cleanup unused data", + "@cacheCleanupUnused": { + "description": "Action title for cleaning unused entries" + }, + "cacheCleanupUnusedSubtitle": "Remove orphaned download history and missing library entries", + "@cacheCleanupUnusedSubtitle": { + "description": "Subtitle for cleanup unused data action" + }, + "cacheCleanupResult": "Cleanup completed: {downloadCount} orphaned downloads, {libraryCount} missing library entries", + "@cacheCleanupResult": { + "description": "Snackbar after unused data cleanup", + "placeholders": { + "downloadCount": { + "type": "int" + }, + "libraryCount": { + "type": "int" + } + } + }, + "cacheRefreshStats": "Refresh stats", + "@cacheRefreshStats": { + "description": "Button label to refresh cache statistics" + }, + "trackSaveCoverArt": "Save Cover Art", + "@trackSaveCoverArt": { + "description": "Menu action - save album cover art as file" + }, + "trackSaveCoverArtSubtitle": "Save album art as .jpg file", + "@trackSaveCoverArtSubtitle": { + "description": "Subtitle for save cover art action" + }, + "trackSaveLyrics": "Save Lyrics (.lrc)", + "@trackSaveLyrics": { + "description": "Menu action - save lyrics as .lrc file" + }, + "trackSaveLyricsSubtitle": "Fetch and save lyrics as .lrc file", + "@trackSaveLyricsSubtitle": { + "description": "Subtitle for save lyrics action" + }, + "trackSaveLyricsProgress": "Saving lyrics...", + "@trackSaveLyricsProgress": { + "description": "Snackbar while saving lyrics to file" + }, + "trackReEnrich": "Re-enrich", + "@trackReEnrich": { + "description": "Menu action - re-embed metadata into audio file" + }, + "trackReEnrichOnlineSubtitle": "Search metadata online and embed into file", + "@trackReEnrichOnlineSubtitle": { + "description": "Subtitle for re-enrich metadata action for local items" + }, + "trackReEnrichFieldsTitle": "Fields to update", + "@trackReEnrichFieldsTitle": { + "description": "Section title for field selection in re-enrich dialog" + }, + "trackReEnrichFieldCover": "Cover Art", + "@trackReEnrichFieldCover": { + "description": "Checkbox label for cover art field in re-enrich" + }, + "trackReEnrichFieldLyrics": "Lyrics", + "@trackReEnrichFieldLyrics": { + "description": "Checkbox label for lyrics field in re-enrich" + }, + "trackReEnrichFieldBasicTags": "Album, Album Artist", + "@trackReEnrichFieldBasicTags": { + "description": "Checkbox label for basic tags in re-enrich (title/artist are never overwritten)" + }, + "trackReEnrichFieldTrackInfo": "Track & Disc Number", + "@trackReEnrichFieldTrackInfo": { + "description": "Checkbox label for track info in re-enrich" + }, + "trackReEnrichFieldReleaseInfo": "Date & ISRC", + "@trackReEnrichFieldReleaseInfo": { + "description": "Checkbox label for release info in re-enrich" + }, + "trackReEnrichFieldExtra": "Genre, Label, Copyright", + "@trackReEnrichFieldExtra": { + "description": "Checkbox label for extra metadata in re-enrich" + }, + "trackReEnrichSelectAll": "Select All", + "@trackReEnrichSelectAll": { + "description": "Select all fields checkbox in re-enrich" + }, + "trackEditMetadata": "Edit Metadata", + "@trackEditMetadata": { + "description": "Menu action - edit embedded metadata" + }, + "trackCoverSaved": "Cover art saved to {fileName}", + "@trackCoverSaved": { + "description": "Snackbar after cover art saved", + "placeholders": { + "fileName": { + "type": "String" + } + } + }, + "trackCoverNoSource": "No cover art source available", + "@trackCoverNoSource": { + "description": "Snackbar when no cover art URL or embedded cover" + }, + "trackLyricsSaved": "Lyrics saved to {fileName}", + "@trackLyricsSaved": { + "description": "Snackbar after lyrics saved", + "placeholders": { + "fileName": { + "type": "String" + } + } + }, + "trackReEnrichProgress": "Re-enriching metadata...", + "@trackReEnrichProgress": { + "description": "Snackbar while re-enriching metadata" + }, + "trackReEnrichSearching": "Searching metadata online...", + "@trackReEnrichSearching": { + "description": "Snackbar while searching metadata from internet for local items" + }, + "trackReEnrichSuccess": "Metadata re-enriched successfully", + "@trackReEnrichSuccess": { + "description": "Snackbar after successful re-enrichment" + }, + "trackReEnrichFfmpegFailed": "FFmpeg metadata embed failed", + "@trackReEnrichFfmpegFailed": { + "description": "Snackbar when FFmpeg embed fails for MP3/Opus" + }, + "queueFlacAction": "Queue FLAC", + "@queueFlacAction": { + "description": "Action/button label for queueing FLAC redownloads for local tracks" + }, + "queueFlacConfirmMessage": "Search online matches for the selected tracks and queue FLAC downloads.\n\nExisting files will not be modified or deleted.\n\nOnly high-confidence matches are queued automatically.\n\n{count} selected", + "@queueFlacConfirmMessage": { + "description": "Confirmation dialog body before queueing FLAC redownloads for local tracks", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "queueFlacFindingProgress": "Finding FLAC matches... ({current}/{total})", + "@queueFlacFindingProgress": { + "description": "Snackbar while resolving remote matches for local FLAC redownloads", + "placeholders": { + "current": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "queueFlacNoReliableMatches": "No reliable online matches found for the selection", + "@queueFlacNoReliableMatches": { + "description": "Snackbar when no safe FLAC redownload matches were found" + }, + "queueFlacQueuedWithSkipped": "Added {addedCount} tracks to queue, skipped {skippedCount}", + "@queueFlacQueuedWithSkipped": { + "description": "Snackbar when some selected local tracks were queued for FLAC redownload and some were skipped", + "placeholders": { + "addedCount": { + "type": "int" + }, + "skippedCount": { + "type": "int" + } + } + }, + "trackSaveFailed": "Failed: {error}", + "@trackSaveFailed": { + "description": "Snackbar when save operation fails", + "placeholders": { + "error": { + "type": "String" + } + } + }, + "trackConvertFormat": "Convert Format", + "@trackConvertFormat": { + "description": "Menu item - convert audio format" + }, + "trackConvertFormatSubtitle": "Convert to MP3, Opus, ALAC, or FLAC", + "@trackConvertFormatSubtitle": { + "description": "Subtitle for convert format menu item" + }, + "trackConvertTitle": "Convert Audio", + "@trackConvertTitle": { + "description": "Title of convert bottom sheet" + }, + "trackConvertTargetFormat": "Target Format", + "@trackConvertTargetFormat": { + "description": "Label for format selection" + }, + "trackConvertBitrate": "Bitrate", + "@trackConvertBitrate": { + "description": "Label for bitrate selection" + }, + "trackConvertConfirmTitle": "Confirm Conversion", + "@trackConvertConfirmTitle": { + "description": "Confirmation dialog title" + }, + "trackConvertConfirmMessage": "Convert from {sourceFormat} to {targetFormat} at {bitrate}?\n\nThe original file will be deleted after conversion.", + "@trackConvertConfirmMessage": { + "description": "Confirmation dialog message", + "placeholders": { + "sourceFormat": { + "type": "String" + }, + "targetFormat": { + "type": "String" + }, + "bitrate": { + "type": "String" + } + } + }, + "trackConvertConfirmMessageLossless": "Convert from {sourceFormat} to {targetFormat}? (Lossless — no quality loss)\n\nThe original file will be deleted after conversion.", + "@trackConvertConfirmMessageLossless": { + "description": "Confirmation dialog message for lossless-to-lossless conversion", + "placeholders": { + "sourceFormat": { + "type": "String" + }, + "targetFormat": { + "type": "String" + } + } + }, + "trackConvertLosslessHint": "Lossless conversion — no quality loss", + "@trackConvertLosslessHint": { + "description": "Hint shown when converting between lossless formats" + }, + "trackConvertConverting": "Converting audio...", + "@trackConvertConverting": { + "description": "Snackbar while converting" + }, + "trackConvertSuccess": "Converted to {format} successfully", + "@trackConvertSuccess": { + "description": "Snackbar after successful conversion", + "placeholders": { + "format": { + "type": "String" + } + } + }, + "trackConvertFailed": "Conversion failed", + "@trackConvertFailed": { + "description": "Snackbar when conversion fails" + }, + "cueSplitTitle": "Split CUE Sheet", + "@cueSplitTitle": { + "description": "Title for CUE split bottom sheet" + }, + "cueSplitSubtitle": "Split CUE+FLAC into individual tracks", + "@cueSplitSubtitle": { + "description": "Subtitle for CUE split menu item" + }, + "cueSplitAlbum": "Album: {album}", + "@cueSplitAlbum": { + "description": "Album name in CUE split sheet", + "placeholders": { + "album": { + "type": "String" + } + } + }, + "cueSplitArtist": "Artist: {artist}", + "@cueSplitArtist": { + "description": "Artist name in CUE split sheet", + "placeholders": { + "artist": { + "type": "String" + } + } + }, + "cueSplitTrackCount": "{count} tracks", + "@cueSplitTrackCount": { + "description": "Number of tracks in CUE sheet", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "cueSplitConfirmTitle": "Split CUE Album", + "@cueSplitConfirmTitle": { + "description": "CUE split confirmation dialog title" + }, + "cueSplitConfirmMessage": "Split \"{album}\" into {count} individual FLAC files?\n\nFiles will be saved to the same directory.", + "@cueSplitConfirmMessage": { + "description": "CUE split confirmation dialog message", + "placeholders": { + "album": { + "type": "String" + }, + "count": { + "type": "int" + } + } + }, + "cueSplitSplitting": "Splitting CUE sheet... ({current}/{total})", + "@cueSplitSplitting": { + "description": "Snackbar while splitting CUE", + "placeholders": { + "current": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "cueSplitSuccess": "Split into {count} tracks successfully", + "@cueSplitSuccess": { + "description": "Snackbar after successful CUE split", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "cueSplitFailed": "CUE split failed", + "@cueSplitFailed": { + "description": "Snackbar when CUE split fails" + }, + "cueSplitNoAudioFile": "Audio file not found for this CUE sheet", + "@cueSplitNoAudioFile": { + "description": "Error when CUE audio file is missing" + }, + "cueSplitButton": "Split into Tracks", + "@cueSplitButton": { + "description": "Button text to start CUE splitting" + }, + "actionCreate": "Create", + "@actionCreate": { + "description": "Generic action button - create" + }, + "collectionFoldersTitle": "My folders", + "@collectionFoldersTitle": { + "description": "Library section title for custom folders" + }, + "collectionWishlist": "Wishlist", + "@collectionWishlist": { + "description": "Custom folder for saved tracks to download later" + }, + "collectionLoved": "Loved", + "@collectionLoved": { + "description": "Custom folder for favorite tracks" + }, + "collectionPlaylists": "Playlists", + "@collectionPlaylists": { + "description": "Custom user playlists folder" + }, + "collectionPlaylist": "Playlist", + "@collectionPlaylist": { + "description": "Single playlist label" + }, + "collectionAddToPlaylist": "Add to playlist", + "@collectionAddToPlaylist": { + "description": "Action to add a track to user playlist" + }, + "collectionCreatePlaylist": "Create playlist", + "@collectionCreatePlaylist": { + "description": "Action to create a new playlist" + }, + "collectionNoPlaylistsYet": "No playlists yet", + "@collectionNoPlaylistsYet": { + "description": "Empty state title when user has no playlists" + }, + "collectionNoPlaylistsSubtitle": "Create a playlist to start categorizing tracks", + "@collectionNoPlaylistsSubtitle": { + "description": "Empty state subtitle when user has no playlists" + }, + "collectionPlaylistTracks": "{count, plural, =1{1 track} other{{count} tracks}}", + "@collectionPlaylistTracks": { + "description": "Track count label for custom playlists", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "collectionAddedToPlaylist": "Added to \"{playlistName}\"", + "@collectionAddedToPlaylist": { + "description": "Snackbar after adding track to playlist", + "placeholders": { + "playlistName": { + "type": "String" + } + } + }, + "collectionAlreadyInPlaylist": "Already in \"{playlistName}\"", + "@collectionAlreadyInPlaylist": { + "description": "Snackbar when track already exists in playlist", + "placeholders": { + "playlistName": { + "type": "String" + } + } + }, + "collectionPlaylistCreated": "Playlist created", + "@collectionPlaylistCreated": { + "description": "Snackbar after creating playlist" + }, + "collectionPlaylistNameHint": "Playlist name", + "@collectionPlaylistNameHint": { + "description": "Hint text for playlist name input" + }, + "collectionPlaylistNameRequired": "Playlist name is required", + "@collectionPlaylistNameRequired": { + "description": "Validation error for empty playlist name" + }, + "collectionRenamePlaylist": "Rename playlist", + "@collectionRenamePlaylist": { + "description": "Action to rename playlist" + }, + "collectionDeletePlaylist": "Delete playlist", + "@collectionDeletePlaylist": { + "description": "Action to delete playlist" + }, + "collectionDeletePlaylistMessage": "Delete \"{playlistName}\" and all tracks inside it?", + "@collectionDeletePlaylistMessage": { + "description": "Confirmation message for deleting playlist", + "placeholders": { + "playlistName": { + "type": "String" + } + } + }, + "collectionPlaylistDeleted": "Playlist deleted", + "@collectionPlaylistDeleted": { + "description": "Snackbar after deleting playlist" + }, + "collectionPlaylistRenamed": "Playlist renamed", + "@collectionPlaylistRenamed": { + "description": "Snackbar after renaming playlist" + }, + "collectionWishlistEmptyTitle": "Wishlist is empty", + "@collectionWishlistEmptyTitle": { + "description": "Wishlist empty state title" + }, + "collectionWishlistEmptySubtitle": "Tap + on tracks to save what you want to download later", + "@collectionWishlistEmptySubtitle": { + "description": "Wishlist empty state subtitle" + }, + "collectionLovedEmptyTitle": "Loved folder is empty", + "@collectionLovedEmptyTitle": { + "description": "Loved empty state title" + }, + "collectionLovedEmptySubtitle": "Tap love on tracks to keep your favorites", + "@collectionLovedEmptySubtitle": { + "description": "Loved empty state subtitle" + }, + "collectionPlaylistEmptyTitle": "Playlist is empty", + "@collectionPlaylistEmptyTitle": { + "description": "Playlist empty state title" + }, + "collectionPlaylistEmptySubtitle": "Long-press + on any track to add it here", + "@collectionPlaylistEmptySubtitle": { + "description": "Playlist empty state subtitle" + }, + "collectionRemoveFromPlaylist": "Remove from playlist", + "@collectionRemoveFromPlaylist": { + "description": "Tooltip for removing track from playlist" + }, + "collectionRemoveFromFolder": "Remove from folder", + "@collectionRemoveFromFolder": { + "description": "Tooltip for removing track from wishlist/loved folder" + }, + "collectionRemoved": "\"{trackName}\" removed", + "@collectionRemoved": { + "description": "Snackbar after removing a track from a collection", + "placeholders": { + "trackName": { + "type": "String" + } + } + }, + "collectionAddedToLoved": "\"{trackName}\" added to Loved", + "@collectionAddedToLoved": { + "description": "Snackbar after adding track to loved folder", + "placeholders": { + "trackName": { + "type": "String" + } + } + }, + "collectionRemovedFromLoved": "\"{trackName}\" removed from Loved", + "@collectionRemovedFromLoved": { + "description": "Snackbar after removing track from loved folder", + "placeholders": { + "trackName": { + "type": "String" + } + } + }, + "collectionAddedToWishlist": "\"{trackName}\" added to Wishlist", + "@collectionAddedToWishlist": { + "description": "Snackbar after adding track to wishlist", + "placeholders": { + "trackName": { + "type": "String" + } + } + }, + "collectionRemovedFromWishlist": "\"{trackName}\" removed from Wishlist", + "@collectionRemovedFromWishlist": { + "description": "Snackbar after removing track from wishlist", + "placeholders": { + "trackName": { + "type": "String" + } + } + }, + "trackOptionAddToLoved": "Add to Loved", + "@trackOptionAddToLoved": { + "description": "Bottom sheet action label - add track to loved folder" + }, + "trackOptionRemoveFromLoved": "Remove from Loved", + "@trackOptionRemoveFromLoved": { + "description": "Bottom sheet action label - remove track from loved folder" + }, + "trackOptionAddToWishlist": "Add to Wishlist", + "@trackOptionAddToWishlist": { + "description": "Bottom sheet action label - add track to wishlist" + }, + "trackOptionRemoveFromWishlist": "Remove from Wishlist", + "@trackOptionRemoveFromWishlist": { + "description": "Bottom sheet action label - remove track from wishlist" + }, + "collectionPlaylistChangeCover": "Change cover image", + "@collectionPlaylistChangeCover": { + "description": "Bottom sheet action to pick a custom cover image for a playlist" + }, + "collectionPlaylistRemoveCover": "Remove cover image", + "@collectionPlaylistRemoveCover": { + "description": "Bottom sheet action to remove custom cover image from a playlist" + }, + "selectionShareCount": "Share {count} {count, plural, =1{track} other{tracks}}", + "@selectionShareCount": { + "description": "Share button text with count in selection mode", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "selectionShareNoFiles": "No shareable files found", + "@selectionShareNoFiles": { + "description": "Snackbar when no selected files exist on disk" + }, + "selectionConvertCount": "Convert {count} {count, plural, =1{track} other{tracks}}", + "@selectionConvertCount": { + "description": "Convert button text with count in selection mode", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "selectionConvertNoConvertible": "No convertible tracks selected", + "@selectionConvertNoConvertible": { + "description": "Snackbar when no selected tracks support conversion" + }, + "selectionBatchConvertConfirmTitle": "Batch Convert", + "@selectionBatchConvertConfirmTitle": { + "description": "Confirmation dialog title for batch conversion" + }, + "selectionBatchConvertConfirmMessage": "Convert {count} {count, plural, =1{track} other{tracks}} to {format} at {bitrate}?\n\nOriginal files will be deleted after conversion.", + "@selectionBatchConvertConfirmMessage": { + "description": "Confirmation dialog message for batch conversion", + "placeholders": { + "count": { + "type": "int" + }, + "format": { + "type": "String" + }, + "bitrate": { + "type": "String" + } + } + }, + "selectionBatchConvertConfirmMessageLossless": "Convert {count} {count, plural, =1{track} other{tracks}} to {format}? (Lossless — no quality loss)\n\nOriginal files will be deleted after conversion.", + "@selectionBatchConvertConfirmMessageLossless": { + "description": "Confirmation dialog message for lossless batch conversion", + "placeholders": { + "count": { + "type": "int" + }, + "format": { + "type": "String" + } + } + }, + "selectionBatchConvertProgress": "Converting {current} of {total}...", + "@selectionBatchConvertProgress": { + "description": "Snackbar during batch conversion progress", + "placeholders": { + "current": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "selectionBatchConvertSuccess": "Converted {success} of {total} tracks to {format}", + "@selectionBatchConvertSuccess": { + "description": "Snackbar after batch conversion completes", + "placeholders": { + "success": { + "type": "int" + }, + "total": { + "type": "int" + }, + "format": { + "type": "String" + } + } + }, + "downloadUseAlbumArtistForFoldersAlbumSubtitle": "Folder named after Album Artist tag", + "@downloadUseAlbumArtistForFoldersAlbumSubtitle": { + "description": "Subtitle when album artist is used for folder names" + }, + "downloadUseAlbumArtistForFoldersTrackSubtitle": "Folder named after Track Artist tag", + "@downloadUseAlbumArtistForFoldersTrackSubtitle": { + "description": "Subtitle when track artist is used for folder names" + }, + "lyricsProvidersTitle": "Lyrics Provider Priority", + "@lyricsProvidersTitle": { + "description": "Settings item title for lyrics provider order" + }, + "lyricsProvidersDescription": "Enable, disable and reorder lyrics sources. Providers are tried top-to-bottom until lyrics are found.", + "@lyricsProvidersDescription": { + "description": "Description on the lyrics provider priority page" + }, + "lyricsProvidersInfoText": "Extension lyrics providers always run before built-in providers. At least one provider must remain enabled.", + "@lyricsProvidersInfoText": { + "description": "Info tip on lyrics provider priority page" + }, + "lyricsProvidersEnabledSection": "Enabled ({count})", + "@lyricsProvidersEnabledSection": { + "description": "Section header for enabled providers", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "lyricsProvidersDisabledSection": "Disabled ({count})", + "@lyricsProvidersDisabledSection": { + "description": "Section header for disabled providers", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "lyricsProvidersAtLeastOne": "At least one provider must remain enabled", + "@lyricsProvidersAtLeastOne": { + "description": "Snackbar when user tries to disable the last enabled provider" + }, + "lyricsProvidersSaved": "Lyrics provider priority saved", + "@lyricsProvidersSaved": { + "description": "Snackbar after saving lyrics provider priority" + }, + "lyricsProvidersDiscardContent": "You have unsaved changes that will be lost.", + "@lyricsProvidersDiscardContent": { + "description": "Body text of the discard-changes dialog on lyrics provider page" + }, + "lyricsProviderLrclibDesc": "Open-source synced lyrics database", + "@lyricsProviderLrclibDesc": { + "description": "Description for LRCLIB provider" + }, + "lyricsProviderNeteaseDesc": "NetEase Cloud Music (good for Asian songs)", + "@lyricsProviderNeteaseDesc": { + "description": "Description for Netease provider" + }, + "lyricsProviderMusixmatchDesc": "Largest lyrics database (multi-language)", + "@lyricsProviderMusixmatchDesc": { + "description": "Description for Musixmatch provider" + }, + "lyricsProviderAppleMusicDesc": "Word-by-word synced lyrics (via proxy)", + "@lyricsProviderAppleMusicDesc": { + "description": "Description for Apple Music provider" + }, + "lyricsProviderQqMusicDesc": "QQ Music (good for Chinese songs, via proxy)", + "@lyricsProviderQqMusicDesc": { + "description": "Description for QQ Music provider" + }, + "lyricsProviderExtensionDesc": "Extension provider", + "@lyricsProviderExtensionDesc": { + "description": "Generic description for extension-based lyrics providers" + }, + "safMigrationTitle": "Storage Update Required", + "@safMigrationTitle": { + "description": "Title of SAF migration dialog" + }, + "safMigrationMessage1": "SpotiFLAC now uses Android Storage Access Framework (SAF) for downloads. This fixes \"permission denied\" errors on Android 10+.", + "@safMigrationMessage1": { + "description": "First paragraph of SAF migration dialog" + }, + "safMigrationMessage2": "Please select your download folder again to switch to the new storage system.", + "@safMigrationMessage2": { + "description": "Second paragraph of SAF migration dialog" + }, + "safMigrationSuccess": "Download folder updated to SAF mode", + "@safMigrationSuccess": { + "description": "Snackbar after successfully migrating to SAF" + }, + "settingsDonate": "Support Development", + "@settingsDonate": { + "description": "Settings menu item - donate page" + }, + "settingsDonateSubtitle": "Buy the developer a coffee", + "@settingsDonateSubtitle": { + "description": "Subtitle for donate menu item" + }, + "tooltipLoveAll": "Love All", + "@tooltipLoveAll": { + "description": "Tooltip for the Love All button on album/playlist screens" + }, + "tooltipAddToPlaylist": "Add to Playlist", + "@tooltipAddToPlaylist": { + "description": "Tooltip for the Add to Playlist button" + }, + "snackbarRemovedTracksFromLoved": "Removed {count} tracks from Loved", + "@snackbarRemovedTracksFromLoved": { + "description": "Snackbar after removing multiple tracks from Loved folder", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "snackbarAddedTracksToLoved": "Added {count} tracks to Loved", + "@snackbarAddedTracksToLoved": { + "description": "Snackbar after adding multiple tracks to Loved folder", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "dialogDownloadAllTitle": "Download All", + "@dialogDownloadAllTitle": { + "description": "Dialog title for bulk download confirmation" + }, + "dialogDownloadAllMessage": "Download {count} tracks?", + "@dialogDownloadAllMessage": { + "description": "Body of the Download All confirmation dialog", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "homeSkipAlreadyDownloaded": "Skip already downloaded songs", + "@homeSkipAlreadyDownloaded": { + "description": "Checkbox label in import dialog to skip already-downloaded songs" + }, + "homeGoToAlbum": "Go to Album", + "@homeGoToAlbum": { + "description": "Context menu item to navigate to the album page" + }, + "homeAlbumInfoUnavailable": "Album info not available", + "@homeAlbumInfoUnavailable": { + "description": "Snackbar when album info cannot be loaded" + }, + "snackbarLoadingCueSheet": "Loading CUE sheet...", + "@snackbarLoadingCueSheet": { + "description": "Snackbar while loading a CUE sheet file" + }, + "snackbarMetadataSaved": "Metadata saved successfully", + "@snackbarMetadataSaved": { + "description": "Snackbar after successfully saving track metadata" + }, + "snackbarFailedToEmbedLyrics": "Failed to embed lyrics", + "@snackbarFailedToEmbedLyrics": { + "description": "Snackbar when lyrics embedding fails" + }, + "snackbarFailedToWriteStorage": "Failed to write back to storage", + "@snackbarFailedToWriteStorage": { + "description": "Snackbar when writing metadata back to file fails" + }, + "snackbarError": "Error: {error}", + "@snackbarError": { + "description": "Generic error snackbar with error detail", + "placeholders": { + "error": { + "type": "String" + } + } + }, + "snackbarNoActionDefined": "No action defined for this button", + "@snackbarNoActionDefined": { + "description": "Snackbar when an extension button has no action configured" + }, + "noTracksFoundForAlbum": "No tracks found for this album", + "@noTracksFoundForAlbum": { + "description": "Empty state message when an album has no tracks" + }, + "downloadLocationSubtitle": "Choose where to save your downloaded tracks", + "@downloadLocationSubtitle": { + "description": "Subtitle shown in the download location picker sheet" + }, + "storageModeAppFolder": "App Folder (Recommended)", + "@storageModeAppFolder": { + "description": "Storage mode option - app-managed folder" + }, + "storageModeAppFolderSubtitle": "Saves to Music/SpotiFLAC by default", + "@storageModeAppFolderSubtitle": { + "description": "Subtitle for app folder storage mode" + }, + "storageModeSaf": "Custom Folder (SAF)", + "@storageModeSaf": { + "description": "Storage mode option - Storage Access Framework" + }, + "storageModeSafSubtitle": "Pick any folder, including SD card", + "@storageModeSafSubtitle": { + "description": "Subtitle for SAF storage mode" + }, + "downloadFilenameDescription": "Use {artist}, {title}, {album}, {track}, {year}, {date}, {disc} as placeholders.", + "@downloadFilenameDescription": { + "description": "Description shown in filename format editor" + }, + "downloadFilenameInsertTag": "Tap to insert tag:", + "@downloadFilenameInsertTag": { + "description": "Label above filename tag chips" + }, + "downloadSeparateSinglesEnabled": "Singles and EPs saved in a separate folder", + "@downloadSeparateSinglesEnabled": { + "description": "Subtitle when separate singles folder is on" + }, + "downloadSeparateSinglesDisabled": "Singles and albums saved in the same folder", + "@downloadSeparateSinglesDisabled": { + "description": "Subtitle when separate singles folder is off" + }, + "downloadArtistNameFilters": "Artist Name Filters", + "@downloadArtistNameFilters": { + "description": "Setting title for artist folder filter options" + }, + "downloadCreatePlaylistSourceFolder": "Playlist Source Folder", + "@downloadCreatePlaylistSourceFolder": { + "description": "Setting to create a subfolder per playlist source" + }, + "downloadCreatePlaylistSourceFolderEnabled": "A subfolder is created for each playlist", + "@downloadCreatePlaylistSourceFolderEnabled": { + "description": "Subtitle when playlist folder is enabled" + }, + "downloadCreatePlaylistSourceFolderDisabled": "All tracks saved directly to download folder", + "@downloadCreatePlaylistSourceFolderDisabled": { + "description": "Subtitle when playlist folder is disabled" + }, + "downloadCreatePlaylistSourceFolderRedundant": "Handled by folder organization setting", + "@downloadCreatePlaylistSourceFolderRedundant": { + "description": "Subtitle when folder organization is already set to playlist" + }, + "downloadSongLinkRegion": "SongLink Region", + "@downloadSongLinkRegion": { + "description": "Setting for SongLink region used during fallback resolution" + }, + "downloadNetworkCompatibilityMode": "Network Compatibility Mode", + "@downloadNetworkCompatibilityMode": { + "description": "Setting for legacy TLS/network handling" + }, + "downloadNetworkCompatibilityModeEnabled": "Using legacy TLS settings for older networks", + "@downloadNetworkCompatibilityModeEnabled": { + "description": "Subtitle when network compatibility mode is on" + }, + "downloadNetworkCompatibilityModeDisabled": "Using standard network settings", + "@downloadNetworkCompatibilityModeDisabled": { + "description": "Subtitle when network compatibility mode is off" + }, + "downloadSelectServiceToEnable": "Select Tidal or Qobuz to enable this option", + "@downloadSelectServiceToEnable": { + "description": "Subtitle when quality picker is disabled due to extension service" + }, + "downloadSelectTidalQobuz": "Select Tidal or Qobuz to choose audio quality", + "@downloadSelectTidalQobuz": { + "description": "Info shown when a non-built-in service is selected" + }, + "downloadEmbedLyricsDisabled": "Enable metadata embedding first", + "@downloadEmbedLyricsDisabled": { + "description": "Subtitle when lyrics embedding is blocked by metadata toggle" + }, + "downloadNeteaseIncludeTranslation": "Netease: Include Translation", + "@downloadNeteaseIncludeTranslation": { + "description": "Setting to include translated lyrics from Netease" + }, + "downloadNeteaseIncludeTranslationEnabled": "Chinese translation lines included", + "@downloadNeteaseIncludeTranslationEnabled": { + "description": "Subtitle when Netease translation is on" + }, + "downloadNeteaseIncludeTranslationDisabled": "Original lyrics only", + "@downloadNeteaseIncludeTranslationDisabled": { + "description": "Subtitle when Netease translation is off" + }, + "downloadNeteaseIncludeRomanization": "Netease: Include Romanization", + "@downloadNeteaseIncludeRomanization": { + "description": "Setting to include romanized lyrics from Netease" + }, + "downloadNeteaseIncludeRomanizationEnabled": "Romanization lines included", + "@downloadNeteaseIncludeRomanizationEnabled": { + "description": "Subtitle when Netease romanization is on" + }, + "downloadNeteaseIncludeRomanizationDisabled": "No romanization", + "@downloadNeteaseIncludeRomanizationDisabled": { + "description": "Subtitle when Netease romanization is off" + }, + "downloadAppleQqMultiPerson": "Apple / QQ: Multi-Person Lyrics", + "@downloadAppleQqMultiPerson": { + "description": "Setting for word-by-word multi-person lyrics from Apple Music and QQ Music" + }, + "downloadAppleQqMultiPersonEnabled": "Speaker labels included for duets and group tracks", + "@downloadAppleQqMultiPersonEnabled": { + "description": "Subtitle when multi-person lyrics is on" + }, + "downloadAppleQqMultiPersonDisabled": "Standard lyrics without speaker labels", + "@downloadAppleQqMultiPersonDisabled": { + "description": "Subtitle when multi-person lyrics is off" + }, + "downloadMusixmatchLanguage": "Musixmatch Language", + "@downloadMusixmatchLanguage": { + "description": "Setting for Musixmatch lyrics translation language" + }, + "downloadMusixmatchLanguageAuto": "Auto (original language)", + "@downloadMusixmatchLanguageAuto": { + "description": "Subtitle when no language is set" + }, + "downloadFilterContributing": "Filter Contributing Artists", + "@downloadFilterContributing": { + "description": "Setting to strip contributing artists from Album Artist folder name" + }, + "downloadFilterContributingEnabled": "Contributing artists removed from Album Artist folder name", + "@downloadFilterContributingEnabled": { + "description": "Subtitle when contributing artist filter is on" + }, + "downloadFilterContributingDisabled": "Full Album Artist string used", + "@downloadFilterContributingDisabled": { + "description": "Subtitle when contributing artist filter is off" + }, + "downloadProvidersNoneEnabled": "No providers enabled", + "@downloadProvidersNoneEnabled": { + "description": "Shown when no lyrics providers are active" + }, + "downloadMusixmatchLanguageCode": "Language code", + "@downloadMusixmatchLanguageCode": { + "description": "Label for Musixmatch language input field" + }, + "downloadMusixmatchLanguageHint": "e.g. en, de, ja", + "@downloadMusixmatchLanguageHint": { + "description": "Placeholder for Musixmatch language input" + }, + "downloadMusixmatchLanguageDesc": "Enter a BCP-47 language code (e.g. en, de, ja) to request translated lyrics from Musixmatch.", + "@downloadMusixmatchLanguageDesc": { + "description": "Description in Musixmatch language picker" + }, + "downloadMusixmatchAuto": "Auto", + "@downloadMusixmatchAuto": { + "description": "Button to clear Musixmatch language (use auto)" + }, + "downloadNetworkAnySubtitle": "Use WiFi or mobile data", + "@downloadNetworkAnySubtitle": { + "description": "Subtitle for any-network option in picker" + }, + "downloadNetworkWifiOnlySubtitle": "Downloads pause when on mobile data", + "@downloadNetworkWifiOnlySubtitle": { + "description": "Subtitle for WiFi-only option in picker" + }, + "downloadSongLinkRegionDesc": "Region used when resolving track links via SongLink. Choose the country where your streaming services are available.", + "@downloadSongLinkRegionDesc": { + "description": "Description in SongLink region picker" + }, + "snackbarUnsupportedAudioFormat": "Unsupported audio format", + "@snackbarUnsupportedAudioFormat": { + "description": "Snackbar when the audio format is not supported for the requested operation" + }, + "cacheRefresh": "Refresh", + "@cacheRefresh": { + "description": "Tooltip for refresh button on cache management page" + }, + "dialogDownloadPlaylistsMessage": "Download {trackCount} {trackCount, plural, =1{track} other{tracks}} from {playlistCount} {playlistCount, plural, =1{playlist} other{playlists}}?", + "@dialogDownloadPlaylistsMessage": { + "description": "Dialog message for bulk playlist download confirmation", + "placeholders": { + "trackCount": { + "type": "int" + }, + "playlistCount": { + "type": "int" + } + } + }, + "bulkDownloadPlaylistsButton": "Download {count} {count, plural, =1{playlist} other{playlists}}", + "@bulkDownloadPlaylistsButton": { + "description": "Button label for bulk downloading selected playlists", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "bulkDownloadSelectPlaylists": "Select playlists to download", + "@bulkDownloadSelectPlaylists": { + "description": "Button label when no playlists are selected for download" + }, + "snackbarSelectedPlaylistsEmpty": "Selected playlists have no tracks", + "@snackbarSelectedPlaylistsEmpty": { + "description": "Snackbar when selected playlists contain no tracks" + }, + "playlistsCount": "{count, plural, =1{1 playlist} other{{count} playlists}}", + "@playlistsCount": { + "description": "Playlist count display", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "editMetadataAutoFill": "Auto-fill from online", + "@editMetadataAutoFill": { + "description": "Section title for selective online metadata auto-fill in the edit metadata sheet" + }, + "editMetadataAutoFillDesc": "Select fields to fill automatically from online metadata", + "@editMetadataAutoFillDesc": { + "description": "Description for the auto-fill section" + }, + "editMetadataAutoFillFetch": "Fetch & Fill", + "@editMetadataAutoFillFetch": { + "description": "Button label to fetch online metadata and fill selected fields" + }, + "editMetadataAutoFillSearching": "Searching online...", + "@editMetadataAutoFillSearching": { + "description": "Snackbar shown while searching for online metadata" + }, + "editMetadataAutoFillNoResults": "No matching metadata found online", + "@editMetadataAutoFillNoResults": { + "description": "Snackbar when online metadata search returns no results" + }, + "editMetadataAutoFillDone": "Filled {count} {count, plural, =1{field} other{fields}} from online metadata", + "@editMetadataAutoFillDone": { + "description": "Snackbar confirming how many fields were auto-filled", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "editMetadataAutoFillNoneSelected": "Select at least one field to auto-fill", + "@editMetadataAutoFillNoneSelected": { + "description": "Snackbar when user taps Fetch without selecting any fields" + }, + "editMetadataFieldTitle": "Title", + "@editMetadataFieldTitle": { + "description": "Chip label for title field in auto-fill selector" + }, + "editMetadataFieldArtist": "Artist", + "@editMetadataFieldArtist": { + "description": "Chip label for artist field in auto-fill selector" + }, + "editMetadataFieldAlbum": "Album", + "@editMetadataFieldAlbum": { + "description": "Chip label for album field in auto-fill selector" + }, + "editMetadataFieldAlbumArtist": "Album Artist", + "@editMetadataFieldAlbumArtist": { + "description": "Chip label for album artist field in auto-fill selector" + }, + "editMetadataFieldDate": "Date", + "@editMetadataFieldDate": { + "description": "Chip label for date field in auto-fill selector" + }, + "editMetadataFieldTrackNum": "Track #", + "@editMetadataFieldTrackNum": { + "description": "Chip label for track number field in auto-fill selector" + }, + "editMetadataFieldDiscNum": "Disc #", + "@editMetadataFieldDiscNum": { + "description": "Chip label for disc number field in auto-fill selector" + }, + "editMetadataFieldGenre": "Genre", + "@editMetadataFieldGenre": { + "description": "Chip label for genre field in auto-fill selector" + }, + "editMetadataFieldIsrc": "ISRC", + "@editMetadataFieldIsrc": { + "description": "Chip label for ISRC field in auto-fill selector" + }, + "editMetadataFieldLabel": "Label", + "@editMetadataFieldLabel": { + "description": "Chip label for label field in auto-fill selector" + }, + "editMetadataFieldCopyright": "Copyright", + "@editMetadataFieldCopyright": { + "description": "Chip label for copyright field in auto-fill selector" + }, + "editMetadataFieldCover": "Cover Art", + "@editMetadataFieldCover": { + "description": "Chip label for cover art field in auto-fill selector" + }, + "editMetadataSelectAll": "All", + "@editMetadataSelectAll": { + "description": "Button to select all fields for auto-fill" + }, + "editMetadataSelectEmpty": "Empty only", + "@editMetadataSelectEmpty": { + "description": "Button to select only fields that are currently empty" + }, + "queueDownloadingCount": "Downloading ({count})", + "@queueDownloadingCount": { + "description": "Header for active downloads section with count", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "queueDownloadedHeader": "Downloaded", + "@queueDownloadedHeader": { + "description": "Header label for downloaded items section in library" + }, + "queueFilteringIndicator": "Filtering...", + "@queueFilteringIndicator": { + "description": "Shown while filter results are being computed" + }, + "queueTrackCount": "{count, plural, =1{1 track} other{{count} tracks}}", + "@queueTrackCount": { + "description": "Track count label with plural support", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "queueAlbumCount": "{count, plural, =1{1 album} other{{count} albums}}", + "@queueAlbumCount": { + "description": "Album count label with plural support", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "queueEmptyAlbums": "No album downloads", + "@queueEmptyAlbums": { + "description": "Empty state title when no album downloads exist" + }, + "queueEmptyAlbumsSubtitle": "Download multiple tracks from an album to see them here", + "@queueEmptyAlbumsSubtitle": { + "description": "Empty state subtitle for album downloads" + }, + "queueEmptySingles": "No single downloads", + "@queueEmptySingles": { + "description": "Empty state title when no single track downloads exist" + }, + "queueEmptySinglesSubtitle": "Single track downloads will appear here", + "@queueEmptySinglesSubtitle": { + "description": "Empty state subtitle for single track downloads" + }, + "queueEmptyHistory": "No download history", + "@queueEmptyHistory": { + "description": "Empty state title when download history is empty" + }, + "queueEmptyHistorySubtitle": "Downloaded tracks will appear here", + "@queueEmptyHistorySubtitle": { + "description": "Empty state subtitle for download history" + }, + "selectionAllPlaylistsSelected": "All playlists selected", + "@selectionAllPlaylistsSelected": { + "description": "Shown when all playlists are selected in selection mode" + }, + "selectionTapPlaylistsToSelect": "Tap playlists to select", + "@selectionTapPlaylistsToSelect": { + "description": "Hint shown in playlist selection mode" + }, + "selectionSelectPlaylistsToDelete": "Select playlists to delete", + "@selectionSelectPlaylistsToDelete": { + "description": "Hint shown when no playlists are selected for deletion" + }, + "audioAnalysisTitle": "Audio Quality Analysis", + "@audioAnalysisTitle": { + "description": "Title for audio analysis section" + }, + "audioAnalysisDescription": "Verify lossless quality with spectrum analysis", + "@audioAnalysisDescription": { + "description": "Description for audio analysis tap-to-analyze prompt" + }, + "audioAnalysisAnalyzing": "Analyzing audio...", + "@audioAnalysisAnalyzing": { + "description": "Loading text while analyzing audio" + }, + "audioAnalysisSampleRate": "Sample Rate", + "@audioAnalysisSampleRate": { + "description": "Sample rate metric label" + }, + "audioAnalysisBitDepth": "Bit Depth", + "@audioAnalysisBitDepth": { + "description": "Bit depth metric label" + }, + "audioAnalysisChannels": "Channels", + "@audioAnalysisChannels": { + "description": "Channels metric label" + }, + "audioAnalysisDuration": "Duration", + "@audioAnalysisDuration": { + "description": "Duration metric label" + }, + "audioAnalysisNyquist": "Nyquist", + "@audioAnalysisNyquist": { + "description": "Nyquist frequency metric label" + }, + "audioAnalysisFileSize": "Size", + "@audioAnalysisFileSize": { + "description": "File size metric label" + }, + "audioAnalysisDynamicRange": "Dynamic Range", + "@audioAnalysisDynamicRange": { + "description": "Dynamic range metric label" + }, + "audioAnalysisPeak": "Peak", + "@audioAnalysisPeak": { + "description": "Peak amplitude metric label" + }, + "audioAnalysisRms": "RMS", + "@audioAnalysisRms": { + "description": "RMS level metric label" + }, + "audioAnalysisSamples": "Samples", + "@audioAnalysisSamples": { + "description": "Total samples metric label" + }, + "extensionsSearchWith": "Search with {providerName}", + "@extensionsSearchWith": { + "description": "Extensions page - subtitle for built-in search provider option", + "placeholders": { + "providerName": { + "type": "String" + } + } + }, + "extensionsHomeFeedProvider": "Home Feed Provider", + "@extensionsHomeFeedProvider": { + "description": "Extensions page - label for home feed provider selector" + }, + "extensionsHomeFeedDescription": "Choose which extension provides the home feed on the main screen", + "@extensionsHomeFeedDescription": { + "description": "Extensions page - description for home feed provider picker" + }, + "extensionsHomeFeedAuto": "Auto", + "@extensionsHomeFeedAuto": { + "description": "Label for auto-selected search provider" + }, + "extensionsHomeFeedAutoSubtitle": "Automatically select the best available", + "@extensionsHomeFeedAutoSubtitle": { + "description": "Extensions page - subtitle for auto home feed option" + }, + "extensionsHomeFeedUse": "Use {extensionName} home feed", + "@extensionsHomeFeedUse": { + "description": "Extensions page - subtitle for a specific extension home feed option", + "placeholders": { + "extensionName": { + "type": "String" + } + } + }, + "extensionsNoHomeFeedExtensions": "No extensions with home feed", + "@extensionsNoHomeFeedExtensions": { + "description": "Extensions page - shown when no installed extension has home feed" + }, + "sortAlphaAsc": "A-Z", + "@sortAlphaAsc": { + "description": "Sort option - alphabetical ascending" + }, + "sortAlphaDesc": "Z-A", + "@sortAlphaDesc": { + "description": "Sort option - alphabetical descending" + }, + "cancelDownloadTitle": "Cancel download?", + "@cancelDownloadTitle": { + "description": "Dialog title when confirming cancellation of an active download" + }, + "cancelDownloadContent": "This will cancel the active download for \"{trackName}\".", + "@cancelDownloadContent": { + "description": "Dialog body when confirming cancellation of an active download", + "placeholders": { + "trackName": { + "type": "String" + } + } + }, + "cancelDownloadKeep": "Keep", + "@cancelDownloadKeep": { + "description": "Dialog button - keep the active download (do not cancel)" + }, + "metadataSaveFailedFfmpeg": "Failed to save metadata via FFmpeg", + "@metadataSaveFailedFfmpeg": { + "description": "Snackbar error when FFmpeg fails to write metadata" + }, + "metadataSaveFailedStorage": "Failed to write metadata back to storage", + "@metadataSaveFailedStorage": { + "description": "Snackbar error when writing metadata file back to storage fails" + }, + "snackbarFolderPickerFailed": "Failed to open folder picker: {error}", + "@snackbarFolderPickerFailed": { + "description": "Snackbar shown when folder picker fails to open", + "placeholders": { + "error": { + "type": "String" + } + } + }, + "errorLoadAlbum": "Failed to load album", + "@errorLoadAlbum": { + "description": "Error state shown when album fails to load" + }, + "errorLoadPlaylist": "Failed to load playlist", + "@errorLoadPlaylist": { + "description": "Error state shown when playlist fails to load" + }, + "errorLoadArtist": "Failed to load artist", + "@errorLoadArtist": { + "description": "Error state shown when artist fails to load" + }, + "notifChannelDownloadName": "Download Progress", + "@notifChannelDownloadName": { + "description": "Android notification channel name for download progress" + }, + "notifChannelDownloadDesc": "Shows download progress for tracks", + "@notifChannelDownloadDesc": { + "description": "Android notification channel description for download progress" + }, + "notifChannelLibraryScanName": "Library Scan", + "@notifChannelLibraryScanName": { + "description": "Android notification channel name for library scan" + }, + "notifChannelLibraryScanDesc": "Shows local library scan progress", + "@notifChannelLibraryScanDesc": { + "description": "Android notification channel description for library scan" + }, + "notifDownloadingTrack": "Downloading {trackName}", + "@notifDownloadingTrack": { + "description": "Notification title while downloading a track", + "placeholders": { + "trackName": { + "type": "String" + } + } + }, + "notifFinalizingTrack": "Finalizing {trackName}", + "@notifFinalizingTrack": { + "description": "Notification title while finalizing (embedding metadata) a track", + "placeholders": { + "trackName": { + "type": "String" + } + } + }, + "notifEmbeddingMetadata": "Embedding metadata...", + "@notifEmbeddingMetadata": { + "description": "Notification body while embedding metadata into a downloaded track" + }, + "notifAlreadyInLibraryCount": "Already in Library ({completed}/{total})", + "@notifAlreadyInLibraryCount": { + "description": "Notification title when track is already in library, with count", + "placeholders": { + "completed": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "notifAlreadyInLibrary": "Already in Library", + "@notifAlreadyInLibrary": { + "description": "Notification title when track is already in library" + }, + "notifDownloadCompleteCount": "Download Complete ({completed}/{total})", + "@notifDownloadCompleteCount": { + "description": "Notification title when download is complete, with count", + "placeholders": { + "completed": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "notifDownloadComplete": "Download Complete", + "@notifDownloadComplete": { + "description": "Notification title when a single download is complete" + }, + "notifDownloadsFinished": "Downloads Finished ({completed} done, {failed} failed)", + "@notifDownloadsFinished": { + "description": "Notification title when queue finishes with some failures", + "placeholders": { + "completed": { + "type": "int" + }, + "failed": { + "type": "int" + } + } + }, + "notifAllDownloadsComplete": "All Downloads Complete", + "@notifAllDownloadsComplete": { + "description": "Notification title when all downloads finish successfully" + }, + "notifTracksDownloadedSuccess": "{count} tracks downloaded successfully", + "@notifTracksDownloadedSuccess": { + "description": "Notification body for queue complete - how many tracks were downloaded", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "notifScanningLibrary": "Scanning local library", + "@notifScanningLibrary": { + "description": "Notification title while scanning local library" + }, + "notifLibraryScanProgressWithTotal": "{scanned}/{total} files • {percentage}%", + "@notifLibraryScanProgressWithTotal": { + "description": "Notification body for library scan progress when total is known", + "placeholders": { + "scanned": { + "type": "int" + }, + "total": { + "type": "int" + }, + "percentage": { + "type": "int" + } + } + }, + "notifLibraryScanProgressNoTotal": "{scanned} files scanned • {percentage}%", + "@notifLibraryScanProgressNoTotal": { + "description": "Notification body for library scan progress when total is unknown", + "placeholders": { + "scanned": { + "type": "int" + }, + "percentage": { + "type": "int" + } + } + }, + "notifLibraryScanComplete": "Library scan complete", + "@notifLibraryScanComplete": { + "description": "Notification title when library scan finishes" + }, + "notifLibraryScanCompleteBody": "{count} tracks indexed", + "@notifLibraryScanCompleteBody": { + "description": "Notification body for library scan complete - number of indexed tracks", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "notifLibraryScanExcluded": "{count} excluded", + "@notifLibraryScanExcluded": { + "description": "Library scan complete suffix - excluded track count", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "notifLibraryScanErrors": "{count} errors", + "@notifLibraryScanErrors": { + "description": "Library scan complete suffix - error count", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "notifLibraryScanFailed": "Library scan failed", + "@notifLibraryScanFailed": { + "description": "Notification title when library scan fails" + }, + "notifLibraryScanCancelled": "Library scan cancelled", + "@notifLibraryScanCancelled": { + "description": "Notification title when library scan is cancelled by the user" + }, + "notifLibraryScanStopped": "Scan stopped before completion.", + "@notifLibraryScanStopped": { + "description": "Notification body when library scan is cancelled" + }, + "notifDownloadingUpdate": "Downloading SpotiFLAC v{version}", + "@notifDownloadingUpdate": { + "description": "Notification title while downloading an app update", + "placeholders": { + "version": { + "type": "String" + } + } + }, + "notifUpdateProgress": "{received} / {total} MB • {percentage}%", + "@notifUpdateProgress": { + "description": "Notification body showing update download progress", + "placeholders": { + "received": { + "type": "String" + }, + "total": { + "type": "String" + }, + "percentage": { + "type": "int" + } + } + }, + "notifUpdateReady": "Update Ready", + "@notifUpdateReady": { + "description": "Notification title when app update download is complete" + }, + "notifUpdateReadyBody": "SpotiFLAC v{version} downloaded. Tap to install.", + "@notifUpdateReadyBody": { + "description": "Notification body when app update is ready to install", + "placeholders": { + "version": { + "type": "String" + } + } + }, + "notifUpdateFailed": "Update Failed", + "@notifUpdateFailed": { + "description": "Notification title when app update download fails" + }, + "notifUpdateFailedBody": "Could not download update. Try again later.", + "@notifUpdateFailedBody": { + "description": "Notification body when app update download fails" + }, + "settingsFiles": "Files & Folders", + "@settingsFiles": { + "description": "Settings menu item - file and folder settings" + }, + "settingsFilesSubtitle": "Download location, filename, folder structure", + "@settingsFilesSubtitle": { + "description": "Subtitle for files & folders settings" + }, + "settingsMetadata": "Metadata", + "@settingsMetadata": { + "description": "Settings menu item - metadata settings" + }, + "settingsMetadataSubtitle": "Cover art, tags, ReplayGain, providers", + "@settingsMetadataSubtitle": { + "description": "Subtitle for metadata settings" + }, + "settingsLyrics": "Lyrics", + "@settingsLyrics": { + "description": "Settings menu item - lyrics settings" + }, + "settingsLyricsSubtitle": "Embed, mode, providers, language options", + "@settingsLyricsSubtitle": { + "description": "Subtitle for lyrics settings" + }, + "settingsApp": "App", + "@settingsApp": { + "description": "Settings menu item - app settings" + }, + "settingsAppSubtitle": "Updates, data, extension repo, debug", + "@settingsAppSubtitle": { + "description": "Subtitle for app settings" + }, + "sectionMetadataProviders": "Providers", + "@sectionMetadataProviders": { + "description": "Settings section header for metadata providers" + }, + "sectionDuplicates": "Duplicates", + "@sectionDuplicates": { + "description": "Settings section header for deduplication" + }, + "sectionLyricsProviderOptions": "Provider Options", + "@sectionLyricsProviderOptions": { + "description": "Settings section header for per-provider lyrics options" + }, + "metadataProvidersTitle": "Metadata Provider Priority", + "@metadataProvidersTitle": { + "description": "Settings item title for metadata provider order" + }, + "metadataProvidersSubtitle": "Drag to set search and metadata source order", + "@metadataProvidersSubtitle": { + "description": "Subtitle for metadata provider priority item" + }, + "downloadDeduplication": "Skip Duplicate Downloads", + "@downloadDeduplication": { + "description": "Setting - skip tracks already in download history" + }, + "downloadDeduplicationEnabled": "Already-downloaded tracks will be skipped", + "@downloadDeduplicationEnabled": { + "description": "Subtitle when deduplication is on" + }, + "downloadDeduplicationDisabled": "All tracks will be downloaded regardless of history", + "@downloadDeduplicationDisabled": { + "description": "Subtitle when deduplication is off" + }, + "downloadFallbackExtensions": "Fallback Extensions", + "@downloadFallbackExtensions": { + "description": "Settings item for configuring fallback extension providers" + }, + "downloadFallbackExtensionsSubtitle": "Choose which extensions can be used as fallback", + "@downloadFallbackExtensionsSubtitle": { + "description": "Subtitle for fallback extensions item" } } diff --git a/lib/l10n/arb/app_es_ES.arb b/lib/l10n/arb/app_es_ES.arb index 286f0a0c..7fccb158 100644 --- a/lib/l10n/arb/app_es_ES.arb +++ b/lib/l10n/arb/app_es_ES.arb @@ -4509,5 +4509,77 @@ "notifUpdateFailedBody": "Could not download update. Try again later.", "@notifUpdateFailedBody": { "description": "Notification body when app update download fails" + }, + "settingsFiles": "Files & Folders", + "@settingsFiles": { + "description": "Settings menu item - file and folder settings" + }, + "settingsFilesSubtitle": "Download location, filename, folder structure", + "@settingsFilesSubtitle": { + "description": "Subtitle for files & folders settings" + }, + "settingsMetadata": "Metadata", + "@settingsMetadata": { + "description": "Settings menu item - metadata settings" + }, + "settingsMetadataSubtitle": "Cover art, tags, ReplayGain, providers", + "@settingsMetadataSubtitle": { + "description": "Subtitle for metadata settings" + }, + "settingsLyrics": "Lyrics", + "@settingsLyrics": { + "description": "Settings menu item - lyrics settings" + }, + "settingsLyricsSubtitle": "Embed, mode, providers, language options", + "@settingsLyricsSubtitle": { + "description": "Subtitle for lyrics settings" + }, + "settingsApp": "App", + "@settingsApp": { + "description": "Settings menu item - app settings" + }, + "settingsAppSubtitle": "Updates, data, extension repo, debug", + "@settingsAppSubtitle": { + "description": "Subtitle for app settings" + }, + "sectionMetadataProviders": "Providers", + "@sectionMetadataProviders": { + "description": "Settings section header for metadata providers" + }, + "sectionDuplicates": "Duplicates", + "@sectionDuplicates": { + "description": "Settings section header for deduplication" + }, + "sectionLyricsProviderOptions": "Provider Options", + "@sectionLyricsProviderOptions": { + "description": "Settings section header for per-provider lyrics options" + }, + "metadataProvidersTitle": "Metadata Provider Priority", + "@metadataProvidersTitle": { + "description": "Settings item title for metadata provider order" + }, + "metadataProvidersSubtitle": "Drag to set search and metadata source order", + "@metadataProvidersSubtitle": { + "description": "Subtitle for metadata provider priority item" + }, + "downloadDeduplication": "Skip Duplicate Downloads", + "@downloadDeduplication": { + "description": "Setting - skip tracks already in download history" + }, + "downloadDeduplicationEnabled": "Already-downloaded tracks will be skipped", + "@downloadDeduplicationEnabled": { + "description": "Subtitle when deduplication is on" + }, + "downloadDeduplicationDisabled": "All tracks will be downloaded regardless of history", + "@downloadDeduplicationDisabled": { + "description": "Subtitle when deduplication is off" + }, + "downloadFallbackExtensions": "Fallback Extensions", + "@downloadFallbackExtensions": { + "description": "Settings item for configuring fallback extension providers" + }, + "downloadFallbackExtensionsSubtitle": "Choose which extensions can be used as fallback", + "@downloadFallbackExtensionsSubtitle": { + "description": "Subtitle for fallback extensions item" } } diff --git a/lib/l10n/arb/app_fr.arb b/lib/l10n/arb/app_fr.arb index dccad293..ab865785 100644 --- a/lib/l10n/arb/app_fr.arb +++ b/lib/l10n/arb/app_fr.arb @@ -4509,5 +4509,77 @@ "notifUpdateFailedBody": "Could not download update. Try again later.", "@notifUpdateFailedBody": { "description": "Notification body when app update download fails" + }, + "settingsFiles": "Files & Folders", + "@settingsFiles": { + "description": "Settings menu item - file and folder settings" + }, + "settingsFilesSubtitle": "Download location, filename, folder structure", + "@settingsFilesSubtitle": { + "description": "Subtitle for files & folders settings" + }, + "settingsMetadata": "Metadata", + "@settingsMetadata": { + "description": "Settings menu item - metadata settings" + }, + "settingsMetadataSubtitle": "Cover art, tags, ReplayGain, providers", + "@settingsMetadataSubtitle": { + "description": "Subtitle for metadata settings" + }, + "settingsLyrics": "Lyrics", + "@settingsLyrics": { + "description": "Settings menu item - lyrics settings" + }, + "settingsLyricsSubtitle": "Embed, mode, providers, language options", + "@settingsLyricsSubtitle": { + "description": "Subtitle for lyrics settings" + }, + "settingsApp": "App", + "@settingsApp": { + "description": "Settings menu item - app settings" + }, + "settingsAppSubtitle": "Updates, data, extension repo, debug", + "@settingsAppSubtitle": { + "description": "Subtitle for app settings" + }, + "sectionMetadataProviders": "Providers", + "@sectionMetadataProviders": { + "description": "Settings section header for metadata providers" + }, + "sectionDuplicates": "Duplicates", + "@sectionDuplicates": { + "description": "Settings section header for deduplication" + }, + "sectionLyricsProviderOptions": "Provider Options", + "@sectionLyricsProviderOptions": { + "description": "Settings section header for per-provider lyrics options" + }, + "metadataProvidersTitle": "Metadata Provider Priority", + "@metadataProvidersTitle": { + "description": "Settings item title for metadata provider order" + }, + "metadataProvidersSubtitle": "Drag to set search and metadata source order", + "@metadataProvidersSubtitle": { + "description": "Subtitle for metadata provider priority item" + }, + "downloadDeduplication": "Skip Duplicate Downloads", + "@downloadDeduplication": { + "description": "Setting - skip tracks already in download history" + }, + "downloadDeduplicationEnabled": "Already-downloaded tracks will be skipped", + "@downloadDeduplicationEnabled": { + "description": "Subtitle when deduplication is on" + }, + "downloadDeduplicationDisabled": "All tracks will be downloaded regardless of history", + "@downloadDeduplicationDisabled": { + "description": "Subtitle when deduplication is off" + }, + "downloadFallbackExtensions": "Fallback Extensions", + "@downloadFallbackExtensions": { + "description": "Settings item for configuring fallback extension providers" + }, + "downloadFallbackExtensionsSubtitle": "Choose which extensions can be used as fallback", + "@downloadFallbackExtensionsSubtitle": { + "description": "Subtitle for fallback extensions item" } } \ No newline at end of file diff --git a/lib/l10n/arb/app_hi.arb b/lib/l10n/arb/app_hi.arb index 355c5c7e..7935892f 100644 --- a/lib/l10n/arb/app_hi.arb +++ b/lib/l10n/arb/app_hi.arb @@ -4509,5 +4509,77 @@ "notifUpdateFailedBody": "Could not download update. Try again later.", "@notifUpdateFailedBody": { "description": "Notification body when app update download fails" + }, + "settingsFiles": "Files & Folders", + "@settingsFiles": { + "description": "Settings menu item - file and folder settings" + }, + "settingsFilesSubtitle": "Download location, filename, folder structure", + "@settingsFilesSubtitle": { + "description": "Subtitle for files & folders settings" + }, + "settingsMetadata": "Metadata", + "@settingsMetadata": { + "description": "Settings menu item - metadata settings" + }, + "settingsMetadataSubtitle": "Cover art, tags, ReplayGain, providers", + "@settingsMetadataSubtitle": { + "description": "Subtitle for metadata settings" + }, + "settingsLyrics": "Lyrics", + "@settingsLyrics": { + "description": "Settings menu item - lyrics settings" + }, + "settingsLyricsSubtitle": "Embed, mode, providers, language options", + "@settingsLyricsSubtitle": { + "description": "Subtitle for lyrics settings" + }, + "settingsApp": "App", + "@settingsApp": { + "description": "Settings menu item - app settings" + }, + "settingsAppSubtitle": "Updates, data, extension repo, debug", + "@settingsAppSubtitle": { + "description": "Subtitle for app settings" + }, + "sectionMetadataProviders": "Providers", + "@sectionMetadataProviders": { + "description": "Settings section header for metadata providers" + }, + "sectionDuplicates": "Duplicates", + "@sectionDuplicates": { + "description": "Settings section header for deduplication" + }, + "sectionLyricsProviderOptions": "Provider Options", + "@sectionLyricsProviderOptions": { + "description": "Settings section header for per-provider lyrics options" + }, + "metadataProvidersTitle": "Metadata Provider Priority", + "@metadataProvidersTitle": { + "description": "Settings item title for metadata provider order" + }, + "metadataProvidersSubtitle": "Drag to set search and metadata source order", + "@metadataProvidersSubtitle": { + "description": "Subtitle for metadata provider priority item" + }, + "downloadDeduplication": "Skip Duplicate Downloads", + "@downloadDeduplication": { + "description": "Setting - skip tracks already in download history" + }, + "downloadDeduplicationEnabled": "Already-downloaded tracks will be skipped", + "@downloadDeduplicationEnabled": { + "description": "Subtitle when deduplication is on" + }, + "downloadDeduplicationDisabled": "All tracks will be downloaded regardless of history", + "@downloadDeduplicationDisabled": { + "description": "Subtitle when deduplication is off" + }, + "downloadFallbackExtensions": "Fallback Extensions", + "@downloadFallbackExtensions": { + "description": "Settings item for configuring fallback extension providers" + }, + "downloadFallbackExtensionsSubtitle": "Choose which extensions can be used as fallback", + "@downloadFallbackExtensionsSubtitle": { + "description": "Subtitle for fallback extensions item" } } \ No newline at end of file diff --git a/lib/l10n/arb/app_id.arb b/lib/l10n/arb/app_id.arb index 667c7d8f..6b0733dd 100644 --- a/lib/l10n/arb/app_id.arb +++ b/lib/l10n/arb/app_id.arb @@ -4509,5 +4509,77 @@ "notifUpdateFailedBody": "Could not download update. Try again later.", "@notifUpdateFailedBody": { "description": "Notification body when app update download fails" + }, + "settingsFiles": "Files & Folders", + "@settingsFiles": { + "description": "Settings menu item - file and folder settings" + }, + "settingsFilesSubtitle": "Download location, filename, folder structure", + "@settingsFilesSubtitle": { + "description": "Subtitle for files & folders settings" + }, + "settingsMetadata": "Metadata", + "@settingsMetadata": { + "description": "Settings menu item - metadata settings" + }, + "settingsMetadataSubtitle": "Cover art, tags, ReplayGain, providers", + "@settingsMetadataSubtitle": { + "description": "Subtitle for metadata settings" + }, + "settingsLyrics": "Lyrics", + "@settingsLyrics": { + "description": "Settings menu item - lyrics settings" + }, + "settingsLyricsSubtitle": "Embed, mode, providers, language options", + "@settingsLyricsSubtitle": { + "description": "Subtitle for lyrics settings" + }, + "settingsApp": "App", + "@settingsApp": { + "description": "Settings menu item - app settings" + }, + "settingsAppSubtitle": "Updates, data, extension repo, debug", + "@settingsAppSubtitle": { + "description": "Subtitle for app settings" + }, + "sectionMetadataProviders": "Providers", + "@sectionMetadataProviders": { + "description": "Settings section header for metadata providers" + }, + "sectionDuplicates": "Duplicates", + "@sectionDuplicates": { + "description": "Settings section header for deduplication" + }, + "sectionLyricsProviderOptions": "Provider Options", + "@sectionLyricsProviderOptions": { + "description": "Settings section header for per-provider lyrics options" + }, + "metadataProvidersTitle": "Metadata Provider Priority", + "@metadataProvidersTitle": { + "description": "Settings item title for metadata provider order" + }, + "metadataProvidersSubtitle": "Drag to set search and metadata source order", + "@metadataProvidersSubtitle": { + "description": "Subtitle for metadata provider priority item" + }, + "downloadDeduplication": "Skip Duplicate Downloads", + "@downloadDeduplication": { + "description": "Setting - skip tracks already in download history" + }, + "downloadDeduplicationEnabled": "Already-downloaded tracks will be skipped", + "@downloadDeduplicationEnabled": { + "description": "Subtitle when deduplication is on" + }, + "downloadDeduplicationDisabled": "All tracks will be downloaded regardless of history", + "@downloadDeduplicationDisabled": { + "description": "Subtitle when deduplication is off" + }, + "downloadFallbackExtensions": "Fallback Extensions", + "@downloadFallbackExtensions": { + "description": "Settings item for configuring fallback extension providers" + }, + "downloadFallbackExtensionsSubtitle": "Choose which extensions can be used as fallback", + "@downloadFallbackExtensionsSubtitle": { + "description": "Subtitle for fallback extensions item" } } \ No newline at end of file diff --git a/lib/l10n/arb/app_ja.arb b/lib/l10n/arb/app_ja.arb index c265d491..75f9e1b0 100644 --- a/lib/l10n/arb/app_ja.arb +++ b/lib/l10n/arb/app_ja.arb @@ -4509,5 +4509,77 @@ "notifUpdateFailedBody": "Could not download update. Try again later.", "@notifUpdateFailedBody": { "description": "Notification body when app update download fails" + }, + "settingsFiles": "Files & Folders", + "@settingsFiles": { + "description": "Settings menu item - file and folder settings" + }, + "settingsFilesSubtitle": "Download location, filename, folder structure", + "@settingsFilesSubtitle": { + "description": "Subtitle for files & folders settings" + }, + "settingsMetadata": "Metadata", + "@settingsMetadata": { + "description": "Settings menu item - metadata settings" + }, + "settingsMetadataSubtitle": "Cover art, tags, ReplayGain, providers", + "@settingsMetadataSubtitle": { + "description": "Subtitle for metadata settings" + }, + "settingsLyrics": "Lyrics", + "@settingsLyrics": { + "description": "Settings menu item - lyrics settings" + }, + "settingsLyricsSubtitle": "Embed, mode, providers, language options", + "@settingsLyricsSubtitle": { + "description": "Subtitle for lyrics settings" + }, + "settingsApp": "App", + "@settingsApp": { + "description": "Settings menu item - app settings" + }, + "settingsAppSubtitle": "Updates, data, extension repo, debug", + "@settingsAppSubtitle": { + "description": "Subtitle for app settings" + }, + "sectionMetadataProviders": "Providers", + "@sectionMetadataProviders": { + "description": "Settings section header for metadata providers" + }, + "sectionDuplicates": "Duplicates", + "@sectionDuplicates": { + "description": "Settings section header for deduplication" + }, + "sectionLyricsProviderOptions": "Provider Options", + "@sectionLyricsProviderOptions": { + "description": "Settings section header for per-provider lyrics options" + }, + "metadataProvidersTitle": "Metadata Provider Priority", + "@metadataProvidersTitle": { + "description": "Settings item title for metadata provider order" + }, + "metadataProvidersSubtitle": "Drag to set search and metadata source order", + "@metadataProvidersSubtitle": { + "description": "Subtitle for metadata provider priority item" + }, + "downloadDeduplication": "Skip Duplicate Downloads", + "@downloadDeduplication": { + "description": "Setting - skip tracks already in download history" + }, + "downloadDeduplicationEnabled": "Already-downloaded tracks will be skipped", + "@downloadDeduplicationEnabled": { + "description": "Subtitle when deduplication is on" + }, + "downloadDeduplicationDisabled": "All tracks will be downloaded regardless of history", + "@downloadDeduplicationDisabled": { + "description": "Subtitle when deduplication is off" + }, + "downloadFallbackExtensions": "Fallback Extensions", + "@downloadFallbackExtensions": { + "description": "Settings item for configuring fallback extension providers" + }, + "downloadFallbackExtensionsSubtitle": "Choose which extensions can be used as fallback", + "@downloadFallbackExtensionsSubtitle": { + "description": "Subtitle for fallback extensions item" } } \ No newline at end of file diff --git a/lib/l10n/arb/app_ko.arb b/lib/l10n/arb/app_ko.arb index 862b39cd..398c09b5 100644 --- a/lib/l10n/arb/app_ko.arb +++ b/lib/l10n/arb/app_ko.arb @@ -4509,5 +4509,77 @@ "notifUpdateFailedBody": "Could not download update. Try again later.", "@notifUpdateFailedBody": { "description": "Notification body when app update download fails" + }, + "settingsFiles": "Files & Folders", + "@settingsFiles": { + "description": "Settings menu item - file and folder settings" + }, + "settingsFilesSubtitle": "Download location, filename, folder structure", + "@settingsFilesSubtitle": { + "description": "Subtitle for files & folders settings" + }, + "settingsMetadata": "Metadata", + "@settingsMetadata": { + "description": "Settings menu item - metadata settings" + }, + "settingsMetadataSubtitle": "Cover art, tags, ReplayGain, providers", + "@settingsMetadataSubtitle": { + "description": "Subtitle for metadata settings" + }, + "settingsLyrics": "Lyrics", + "@settingsLyrics": { + "description": "Settings menu item - lyrics settings" + }, + "settingsLyricsSubtitle": "Embed, mode, providers, language options", + "@settingsLyricsSubtitle": { + "description": "Subtitle for lyrics settings" + }, + "settingsApp": "App", + "@settingsApp": { + "description": "Settings menu item - app settings" + }, + "settingsAppSubtitle": "Updates, data, extension repo, debug", + "@settingsAppSubtitle": { + "description": "Subtitle for app settings" + }, + "sectionMetadataProviders": "Providers", + "@sectionMetadataProviders": { + "description": "Settings section header for metadata providers" + }, + "sectionDuplicates": "Duplicates", + "@sectionDuplicates": { + "description": "Settings section header for deduplication" + }, + "sectionLyricsProviderOptions": "Provider Options", + "@sectionLyricsProviderOptions": { + "description": "Settings section header for per-provider lyrics options" + }, + "metadataProvidersTitle": "Metadata Provider Priority", + "@metadataProvidersTitle": { + "description": "Settings item title for metadata provider order" + }, + "metadataProvidersSubtitle": "Drag to set search and metadata source order", + "@metadataProvidersSubtitle": { + "description": "Subtitle for metadata provider priority item" + }, + "downloadDeduplication": "Skip Duplicate Downloads", + "@downloadDeduplication": { + "description": "Setting - skip tracks already in download history" + }, + "downloadDeduplicationEnabled": "Already-downloaded tracks will be skipped", + "@downloadDeduplicationEnabled": { + "description": "Subtitle when deduplication is on" + }, + "downloadDeduplicationDisabled": "All tracks will be downloaded regardless of history", + "@downloadDeduplicationDisabled": { + "description": "Subtitle when deduplication is off" + }, + "downloadFallbackExtensions": "Fallback Extensions", + "@downloadFallbackExtensions": { + "description": "Settings item for configuring fallback extension providers" + }, + "downloadFallbackExtensionsSubtitle": "Choose which extensions can be used as fallback", + "@downloadFallbackExtensionsSubtitle": { + "description": "Subtitle for fallback extensions item" } } \ No newline at end of file diff --git a/lib/l10n/arb/app_nl.arb b/lib/l10n/arb/app_nl.arb index 5e4be112..b9cbaa09 100644 --- a/lib/l10n/arb/app_nl.arb +++ b/lib/l10n/arb/app_nl.arb @@ -4509,5 +4509,77 @@ "notifUpdateFailedBody": "Could not download update. Try again later.", "@notifUpdateFailedBody": { "description": "Notification body when app update download fails" + }, + "settingsFiles": "Files & Folders", + "@settingsFiles": { + "description": "Settings menu item - file and folder settings" + }, + "settingsFilesSubtitle": "Download location, filename, folder structure", + "@settingsFilesSubtitle": { + "description": "Subtitle for files & folders settings" + }, + "settingsMetadata": "Metadata", + "@settingsMetadata": { + "description": "Settings menu item - metadata settings" + }, + "settingsMetadataSubtitle": "Cover art, tags, ReplayGain, providers", + "@settingsMetadataSubtitle": { + "description": "Subtitle for metadata settings" + }, + "settingsLyrics": "Lyrics", + "@settingsLyrics": { + "description": "Settings menu item - lyrics settings" + }, + "settingsLyricsSubtitle": "Embed, mode, providers, language options", + "@settingsLyricsSubtitle": { + "description": "Subtitle for lyrics settings" + }, + "settingsApp": "App", + "@settingsApp": { + "description": "Settings menu item - app settings" + }, + "settingsAppSubtitle": "Updates, data, extension repo, debug", + "@settingsAppSubtitle": { + "description": "Subtitle for app settings" + }, + "sectionMetadataProviders": "Providers", + "@sectionMetadataProviders": { + "description": "Settings section header for metadata providers" + }, + "sectionDuplicates": "Duplicates", + "@sectionDuplicates": { + "description": "Settings section header for deduplication" + }, + "sectionLyricsProviderOptions": "Provider Options", + "@sectionLyricsProviderOptions": { + "description": "Settings section header for per-provider lyrics options" + }, + "metadataProvidersTitle": "Metadata Provider Priority", + "@metadataProvidersTitle": { + "description": "Settings item title for metadata provider order" + }, + "metadataProvidersSubtitle": "Drag to set search and metadata source order", + "@metadataProvidersSubtitle": { + "description": "Subtitle for metadata provider priority item" + }, + "downloadDeduplication": "Skip Duplicate Downloads", + "@downloadDeduplication": { + "description": "Setting - skip tracks already in download history" + }, + "downloadDeduplicationEnabled": "Already-downloaded tracks will be skipped", + "@downloadDeduplicationEnabled": { + "description": "Subtitle when deduplication is on" + }, + "downloadDeduplicationDisabled": "All tracks will be downloaded regardless of history", + "@downloadDeduplicationDisabled": { + "description": "Subtitle when deduplication is off" + }, + "downloadFallbackExtensions": "Fallback Extensions", + "@downloadFallbackExtensions": { + "description": "Settings item for configuring fallback extension providers" + }, + "downloadFallbackExtensionsSubtitle": "Choose which extensions can be used as fallback", + "@downloadFallbackExtensionsSubtitle": { + "description": "Subtitle for fallback extensions item" } } \ No newline at end of file diff --git a/lib/l10n/arb/app_pt.arb b/lib/l10n/arb/app_pt.arb index 1f41f8f7..de1ff199 100644 --- a/lib/l10n/arb/app_pt.arb +++ b/lib/l10n/arb/app_pt.arb @@ -1728,5 +1728,2858 @@ "type": "int" } } + }, + "navLibrary": "Library", + "@navLibrary": { + "description": "Bottom navigation - Library tab" + }, + "historySearchHint": "Search history...", + "@historySearchHint": { + "description": "Search bar placeholder in history" + }, + "downloadSingleFilenameFormat": "Single Filename Format", + "@downloadSingleFilenameFormat": { + "description": "Setting for output filename pattern for singles/EPs" + }, + "downloadSingleFilenameFormatDescription": "Filename pattern for singles and EPs. Uses the same tags as the album format.", + "@downloadSingleFilenameFormatDescription": { + "description": "Subtitle description for single filename format setting" + }, + "optionsDefaultSearchTab": "Default Search Tab", + "@optionsDefaultSearchTab": { + "description": "Title for the preferred default search tab setting" + }, + "optionsDefaultSearchTabSubtitle": "Choose which tab opens first for new search results.", + "@optionsDefaultSearchTabSubtitle": { + "description": "Subtitle for the preferred default search tab setting" + }, + "optionsReplayGain": "ReplayGain", + "@optionsReplayGain": { + "description": "Title for ReplayGain setting toggle" + }, + "optionsReplayGainSubtitleOn": "Scan loudness and embed ReplayGain tags (EBU R128)", + "@optionsReplayGainSubtitleOn": { + "description": "Subtitle when ReplayGain is enabled" + }, + "optionsReplayGainSubtitleOff": "Disabled: no loudness normalization tags", + "@optionsReplayGainSubtitleOff": { + "description": "Subtitle when ReplayGain is disabled" + }, + "optionsArtistTagMode": "Artist Tag Mode", + "@optionsArtistTagMode": { + "description": "Setting title for how artist metadata is written into files" + }, + "optionsArtistTagModeDescription": "Choose how multiple artists are written into embedded tags.", + "@optionsArtistTagModeDescription": { + "description": "Bottom-sheet description for artist tag mode setting" + }, + "optionsArtistTagModeJoined": "Single joined value", + "@optionsArtistTagModeJoined": { + "description": "Artist tag mode option that joins multiple artists into one value" + }, + "optionsArtistTagModeJoinedSubtitle": "Write one ARTIST value like \"Artist A, Artist B\" for maximum player compatibility.", + "@optionsArtistTagModeJoinedSubtitle": { + "description": "Subtitle for joined artist tag mode" + }, + "optionsArtistTagModeSplitVorbis": "Split tags for FLAC/Opus", + "@optionsArtistTagModeSplitVorbis": { + "description": "Artist tag mode option that writes repeated ARTIST tags for Vorbis formats" + }, + "optionsArtistTagModeSplitVorbisSubtitle": "Write one artist tag per artist for FLAC and Opus; MP3 and M4A stay joined.", + "@optionsArtistTagModeSplitVorbisSubtitle": { + "description": "Subtitle for split Vorbis artist tag mode" + }, + "optionsSpotifyDeprecationWarning": "Spotify search will be deprecated on March 3, 2026 due to Spotify API changes. Please switch to Deezer.", + "@optionsSpotifyDeprecationWarning": { + "description": "Warning about Spotify API deprecation" + }, + "aboutTranslators": "Translators", + "@aboutTranslators": { + "description": "Section for translators" + }, + "aboutTelegramChannel": "Telegram Channel", + "@aboutTelegramChannel": { + "description": "Link to Telegram channel" + }, + "aboutTelegramChannelSubtitle": "Announcements and updates", + "@aboutTelegramChannelSubtitle": { + "description": "Subtitle for Telegram channel" + }, + "aboutTelegramChat": "Telegram Community", + "@aboutTelegramChat": { + "description": "Link to Telegram chat group" + }, + "aboutTelegramChatSubtitle": "Chat with other users", + "@aboutTelegramChatSubtitle": { + "description": "Subtitle for Telegram chat" + }, + "aboutSocial": "Social", + "@aboutSocial": { + "description": "Section for social links" + }, + "aboutSjdonadoDesc": "Creator of I Don't Have Spotify (IDHS). The fallback link resolver that saves the day!", + "@aboutSjdonadoDesc": { + "description": "Credit description for sjdonado" + }, + "aboutSpotiSaver": "SpotiSaver", + "@aboutSpotiSaver": { + "description": "Name of SpotiSaver API service - DO NOT TRANSLATE" + }, + "aboutSpotiSaverDesc": "Tidal Hi-Res FLAC streaming endpoints. A key piece of the lossless puzzle!", + "@aboutSpotiSaverDesc": { + "description": "Credit for SpotiSaver API" + }, + "artistPopular": "Popular", + "@artistPopular": { + "description": "Section header for popular/top tracks" + }, + "artistMonthlyListeners": "{count} monthly listeners", + "@artistMonthlyListeners": { + "description": "Monthly listener count display", + "placeholders": { + "count": { + "type": "String", + "description": "Formatted listener count" + } + } + }, + "setupIcloudNotSupported": "iCloud Drive is not supported. Please use the app Documents folder.", + "@setupIcloudNotSupported": { + "description": "Error when user selects iCloud Drive on iOS" + }, + "dialogDownload": "Download", + "@dialogDownload": { + "description": "Confirm button in Download All dialog" + }, + "csvImportTracks": "{count} tracks from CSV", + "@csvImportTracks": { + "description": "Label shown in quality picker for CSV import", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "snackbarAlreadyInLibrary": "\"{trackName}\" already exists in your library", + "@snackbarAlreadyInLibrary": { + "description": "Snackbar - track already exists in local library", + "placeholders": { + "trackName": { + "type": "String" + } + } + }, + "errorUrlNotRecognized": "Link not recognized", + "@errorUrlNotRecognized": { + "description": "Error title - URL not handled by any extension or service" + }, + "errorUrlNotRecognizedMessage": "This link is not supported. Make sure the URL is correct and a compatible extension is installed.", + "@errorUrlNotRecognizedMessage": { + "description": "Error message - URL not recognized explanation" + }, + "errorUrlFetchFailed": "Failed to load content from this link. Please try again.", + "@errorUrlFetchFailed": { + "description": "Error message - generic URL fetch failure" + }, + "searchSortTitle": "Sort Results", + "@searchSortTitle": { + "description": "Bottom sheet title for search sort options" + }, + "searchSortDefault": "Default", + "@searchSortDefault": { + "description": "Sort option - default API order" + }, + "searchSortTitleAZ": "Title (A-Z)", + "@searchSortTitleAZ": { + "description": "Sort option - title ascending" + }, + "searchSortTitleZA": "Title (Z-A)", + "@searchSortTitleZA": { + "description": "Sort option - title descending" + }, + "searchSortArtistAZ": "Artist (A-Z)", + "@searchSortArtistAZ": { + "description": "Sort option - artist ascending" + }, + "searchSortArtistZA": "Artist (Z-A)", + "@searchSortArtistZA": { + "description": "Sort option - artist descending" + }, + "searchSortDurationShort": "Duration (Shortest)", + "@searchSortDurationShort": { + "description": "Sort option - shortest duration first" + }, + "searchSortDurationLong": "Duration (Longest)", + "@searchSortDurationLong": { + "description": "Sort option - longest duration first" + }, + "searchSortDateOldest": "Release Date (Oldest)", + "@searchSortDateOldest": { + "description": "Sort option - oldest release first" + }, + "searchSortDateNewest": "Release Date (Newest)", + "@searchSortDateNewest": { + "description": "Sort option - newest release first" + }, + "filenameShowAdvancedTags": "Show advanced tags", + "@filenameShowAdvancedTags": { + "description": "Toggle label for showing advanced filename tags" + }, + "filenameShowAdvancedTagsDescription": "Enable formatted tags for track padding and date patterns", + "@filenameShowAdvancedTagsDescription": { + "description": "Description for advanced filename tag toggle" + }, + "folderOrganizationByPlaylist": "By Playlist", + "@folderOrganizationByPlaylist": { + "description": "Folder option - playlist folders" + }, + "folderOrganizationByPlaylistSubtitle": "Separate folder for each playlist", + "@folderOrganizationByPlaylistSubtitle": { + "description": "Subtitle for playlist folder option" + }, + "providerPriorityFallbackExtensionsTitle": "Extension Fallback", + "@providerPriorityFallbackExtensionsTitle": { + "description": "Section title for choosing which download extensions can be used as fallback providers" + }, + "providerPriorityFallbackExtensionsDescription": "Choose which installed download extensions can be used during automatic fallback. Built-in providers still follow the priority order above.", + "@providerPriorityFallbackExtensionsDescription": { + "description": "Section description for extension fallback selection" + }, + "providerPriorityFallbackExtensionsHint": "Only enabled extensions with download-provider capability are listed here.", + "@providerPriorityFallbackExtensionsHint": { + "description": "Hint below the extension fallback selection list" + }, + "sectionLyrics": "Lyrics", + "@sectionLyrics": { + "description": "Settings section header" + }, + "lyricsMode": "Lyrics Mode", + "@lyricsMode": { + "description": "Setting - how to save lyrics" + }, + "lyricsModeDescription": "Choose how lyrics are saved with your downloads", + "@lyricsModeDescription": { + "description": "Lyrics mode picker description" + }, + "lyricsModeEmbed": "Embed in file", + "@lyricsModeEmbed": { + "description": "Lyrics mode option - embed in audio file" + }, + "lyricsModeEmbedSubtitle": "Lyrics stored inside FLAC metadata", + "@lyricsModeEmbedSubtitle": { + "description": "Subtitle for embed option" + }, + "lyricsModeExternal": "External .lrc file", + "@lyricsModeExternal": { + "description": "Lyrics mode option - separate LRC file" + }, + "lyricsModeExternalSubtitle": "Separate .lrc file for players like Samsung Music", + "@lyricsModeExternalSubtitle": { + "description": "Subtitle for external option" + }, + "lyricsModeBoth": "Both", + "@lyricsModeBoth": { + "description": "Lyrics mode option - embed and external" + }, + "lyricsModeBothSubtitle": "Embed and save .lrc file", + "@lyricsModeBothSubtitle": { + "description": "Subtitle for both option" + }, + "trackGenre": "Genre", + "@trackGenre": { + "description": "Metadata label - music genre" + }, + "trackLabel": "Label", + "@trackLabel": { + "description": "Metadata label - record label" + }, + "trackCopyright": "Copyright", + "@trackCopyright": { + "description": "Metadata label - copyright information" + }, + "trackLyricsNotInFile": "No lyrics found in this file", + "@trackLyricsNotInFile": { + "description": "Message when no embedded lyrics in audio file" + }, + "trackFetchOnlineLyrics": "Fetch from Online", + "@trackFetchOnlineLyrics": { + "description": "Action - fetch lyrics from online providers" + }, + "trackEmbedLyrics": "Embed Lyrics", + "@trackEmbedLyrics": { + "description": "Action - embed lyrics into audio file" + }, + "trackLyricsEmbedded": "Lyrics embedded successfully", + "@trackLyricsEmbedded": { + "description": "Snackbar - lyrics saved to file" + }, + "trackInstrumental": "Instrumental track", + "@trackInstrumental": { + "description": "Message when track is instrumental (no lyrics)" + }, + "storeAddRepoTitle": "Add Extension Repository", + "@storeAddRepoTitle": { + "description": "Store setup screen - heading when no repo is configured" + }, + "storeAddRepoDescription": "Enter a GitHub repository URL that contains a registry.json file to browse and install extensions.", + "@storeAddRepoDescription": { + "description": "Store setup screen - explanatory text" + }, + "storeRepoUrlLabel": "Repository URL", + "@storeRepoUrlLabel": { + "description": "Label for the repository URL input field" + }, + "storeRepoUrlHint": "https://github.com/user/repo", + "@storeRepoUrlHint": { + "description": "Hint/placeholder for the repository URL input field" + }, + "storeRepoUrlHelper": "e.g. https://github.com/user/extensions-repo", + "@storeRepoUrlHelper": { + "description": "Helper text below the repository URL input field" + }, + "storeAddRepoButton": "Add Repository", + "@storeAddRepoButton": { + "description": "Button to submit a new repository URL" + }, + "storeChangeRepoTooltip": "Change repository", + "@storeChangeRepoTooltip": { + "description": "Tooltip for the change-repository icon button in the app bar" + }, + "storeRepoDialogTitle": "Extension Repository", + "@storeRepoDialogTitle": { + "description": "Title of the change/remove repository dialog" + }, + "storeRepoDialogCurrent": "Current repository:", + "@storeRepoDialogCurrent": { + "description": "Label shown above the current repository URL in the dialog" + }, + "storeNewRepoUrlLabel": "New Repository URL", + "@storeNewRepoUrlLabel": { + "description": "Label for the new repository URL field inside the dialog" + }, + "storeLoadError": "Failed to load repository", + "@storeLoadError": { + "description": "Error heading when the store cannot be loaded" + }, + "storeEmptyNoExtensions": "No extensions available", + "@storeEmptyNoExtensions": { + "description": "Message when store has no extensions" + }, + "storeEmptyNoResults": "No extensions found", + "@storeEmptyNoResults": { + "description": "Message when search/filter returns no results" + }, + "extensionsFallbackTitle": "Fallback Extensions", + "@extensionsFallbackTitle": { + "description": "Setting and page title for choosing which download extensions can be used during fallback" + }, + "extensionsFallbackSubtitle": "Choose which installed download extensions can be used as fallback", + "@extensionsFallbackSubtitle": { + "description": "Subtitle for download fallback extensions menu" + }, + "downloadLossy320": "Lossy 320kbps", + "@downloadLossy320": { + "description": "Quality option label for Tidal lossy 320kbps" + }, + "downloadLossyFormat": "Lossy Format", + "@downloadLossyFormat": { + "description": "Setting title to pick output format for Tidal lossy downloads" + }, + "downloadLossy320Format": "Lossy 320kbps Format", + "@downloadLossy320Format": { + "description": "Title of the Tidal lossy format picker bottom sheet" + }, + "downloadLossy320FormatDesc": "Choose the output format for Tidal 320kbps lossy downloads. The original AAC stream will be converted to your selected format.", + "@downloadLossy320FormatDesc": { + "description": "Description in the Tidal lossy format picker" + }, + "downloadLossyMp3": "MP3 320kbps", + "@downloadLossyMp3": { + "description": "Tidal lossy format option - MP3 320kbps" + }, + "downloadLossyMp3Subtitle": "Best compatibility, ~10MB per track", + "@downloadLossyMp3Subtitle": { + "description": "Subtitle for MP3 320kbps Tidal lossy option" + }, + "downloadLossyOpus256": "Opus 256kbps", + "@downloadLossyOpus256": { + "description": "Tidal lossy format option - Opus 256kbps" + }, + "downloadLossyOpus256Subtitle": "Best quality Opus, ~8MB per track", + "@downloadLossyOpus256Subtitle": { + "description": "Subtitle for Opus 256kbps Tidal lossy option" + }, + "downloadLossyOpus128": "Opus 128kbps", + "@downloadLossyOpus128": { + "description": "Tidal lossy format option - Opus 128kbps" + }, + "downloadLossyOpus128Subtitle": "Smallest size, ~4MB per track", + "@downloadLossyOpus128Subtitle": { + "description": "Subtitle for Opus 128kbps Tidal lossy option" + }, + "downloadUseAlbumArtistForFolders": "Use Album Artist for folders", + "@downloadUseAlbumArtistForFolders": { + "description": "Setting - choose whether artist folders use Album Artist or Track Artist" + }, + "downloadUsePrimaryArtistOnly": "Primary artist only for folders", + "@downloadUsePrimaryArtistOnly": { + "description": "Setting - strip featured artists from folder name" + }, + "downloadUsePrimaryArtistOnlyEnabled": "Featured artists removed from folder name (e.g. Justin Bieber, Quavo → Justin Bieber)", + "@downloadUsePrimaryArtistOnlyEnabled": { + "description": "Subtitle when primary artist only is enabled" + }, + "downloadUsePrimaryArtistOnlyDisabled": "Full artist string used for folder name", + "@downloadUsePrimaryArtistOnlyDisabled": { + "description": "Subtitle when primary artist only is disabled" + }, + "settingsAutoExportFailed": "Auto-export failed downloads", + "@settingsAutoExportFailed": { + "description": "Setting toggle for auto-export" + }, + "settingsAutoExportFailedSubtitle": "Save failed downloads to TXT file automatically", + "@settingsAutoExportFailedSubtitle": { + "description": "Subtitle for auto-export setting" + }, + "settingsDownloadNetwork": "Download Network", + "@settingsDownloadNetwork": { + "description": "Setting for network type preference" + }, + "settingsDownloadNetworkAny": "WiFi + Mobile Data", + "@settingsDownloadNetworkAny": { + "description": "Network option - use any connection" + }, + "settingsDownloadNetworkWifiOnly": "WiFi Only", + "@settingsDownloadNetworkWifiOnly": { + "description": "Network option - only use WiFi" + }, + "settingsDownloadNetworkSubtitle": "Choose which network to use for downloads. When set to WiFi Only, downloads will pause on mobile data.", + "@settingsDownloadNetworkSubtitle": { + "description": "Subtitle explaining network preference" + }, + "albumFolderArtistAlbumSingles": "Artist / Album + Singles", + "@albumFolderArtistAlbumSingles": { + "description": "Album folder option with singles inside artist" + }, + "albumFolderArtistAlbumSinglesSubtitle": "Artist/Album/ and Artist/Singles/", + "@albumFolderArtistAlbumSinglesSubtitle": { + "description": "Folder structure example" + }, + "albumFolderArtistAlbumFlat": "Artist / Album (Singles flat)", + "@albumFolderArtistAlbumFlat": { + "description": "Album folder option with singles directly in artist folder" + }, + "albumFolderArtistAlbumFlatSubtitle": "Artist/Album/ and Artist/song.flac", + "@albumFolderArtistAlbumFlatSubtitle": { + "description": "Folder structure example for flat singles" + }, + "downloadedAlbumDiscHeader": "Disc {discNumber}", + "@downloadedAlbumDiscHeader": { + "description": "Header for disc separator in multi-disc albums", + "placeholders": { + "discNumber": { + "type": "int", + "example": "1" + } + } + }, + "recentTypeArtist": "Artist", + "@recentTypeArtist": { + "description": "Recent access item type - artist" + }, + "recentTypeAlbum": "Album", + "@recentTypeAlbum": { + "description": "Recent access item type - album" + }, + "recentTypeSong": "Song", + "@recentTypeSong": { + "description": "Recent access item type - song/track" + }, + "recentTypePlaylist": "Playlist", + "@recentTypePlaylist": { + "description": "Recent access item type - playlist" + }, + "recentEmpty": "No recent items yet", + "@recentEmpty": { + "description": "Empty state text for recent access list" + }, + "recentShowAllDownloads": "Show All Downloads", + "@recentShowAllDownloads": { + "description": "Button label to unhide hidden downloads in recent access" + }, + "recentPlaylistInfo": "Playlist: {name}", + "@recentPlaylistInfo": { + "description": "Snackbar message when tapping playlist in recent access", + "placeholders": { + "name": { + "type": "String", + "description": "Playlist name" + } + } + }, + "discographyDownload": "Download Discography", + "@discographyDownload": { + "description": "Button - download artist discography" + }, + "discographyDownloadAll": "Download All", + "@discographyDownloadAll": { + "description": "Option - download entire discography" + }, + "discographyDownloadAllSubtitle": "{count} tracks from {albumCount} releases", + "@discographyDownloadAllSubtitle": { + "description": "Subtitle showing total tracks and albums", + "placeholders": { + "count": { + "type": "int" + }, + "albumCount": { + "type": "int" + } + } + }, + "discographyAlbumsOnly": "Albums Only", + "@discographyAlbumsOnly": { + "description": "Option - download only albums" + }, + "discographyAlbumsOnlySubtitle": "{count} tracks from {albumCount} albums", + "@discographyAlbumsOnlySubtitle": { + "description": "Subtitle showing album tracks count", + "placeholders": { + "count": { + "type": "int" + }, + "albumCount": { + "type": "int" + } + } + }, + "discographySinglesOnly": "Singles & EPs Only", + "@discographySinglesOnly": { + "description": "Option - download only singles" + }, + "discographySinglesOnlySubtitle": "{count} tracks from {albumCount} singles", + "@discographySinglesOnlySubtitle": { + "description": "Subtitle showing singles tracks count", + "placeholders": { + "count": { + "type": "int" + }, + "albumCount": { + "type": "int" + } + } + }, + "discographySelectAlbums": "Select Albums...", + "@discographySelectAlbums": { + "description": "Option - manually select albums to download" + }, + "discographySelectAlbumsSubtitle": "Choose specific albums or singles", + "@discographySelectAlbumsSubtitle": { + "description": "Subtitle for select albums option" + }, + "discographyFetchingTracks": "Fetching tracks...", + "@discographyFetchingTracks": { + "description": "Progress - fetching album tracks" + }, + "discographyFetchingAlbum": "Fetching {current} of {total}...", + "@discographyFetchingAlbum": { + "description": "Progress - fetching specific album", + "placeholders": { + "current": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "discographySelectedCount": "{count} selected", + "@discographySelectedCount": { + "description": "Selection count badge", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "discographyDownloadSelected": "Download Selected", + "@discographyDownloadSelected": { + "description": "Button - download selected albums" + }, + "discographyAddedToQueue": "Added {count} tracks to queue", + "@discographyAddedToQueue": { + "description": "Snackbar - tracks added from discography", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "discographySkippedDownloaded": "{added} added, {skipped} already downloaded", + "@discographySkippedDownloaded": { + "description": "Snackbar - with skipped tracks count", + "placeholders": { + "added": { + "type": "int" + }, + "skipped": { + "type": "int" + } + } + }, + "discographyNoAlbums": "No albums available", + "@discographyNoAlbums": { + "description": "Error - no albums found for artist" + }, + "discographyFailedToFetch": "Failed to fetch some albums", + "@discographyFailedToFetch": { + "description": "Error - some albums failed to load" + }, + "sectionStorageAccess": "Storage Access", + "@sectionStorageAccess": { + "description": "Section header for storage access settings" + }, + "allFilesAccess": "All Files Access", + "@allFilesAccess": { + "description": "Toggle for MANAGE_EXTERNAL_STORAGE permission" + }, + "allFilesAccessEnabledSubtitle": "Can write to any folder", + "@allFilesAccessEnabledSubtitle": { + "description": "Subtitle when all files access is enabled" + }, + "allFilesAccessDisabledSubtitle": "Limited to media folders only", + "@allFilesAccessDisabledSubtitle": { + "description": "Subtitle when all files access is disabled" + }, + "allFilesAccessDescription": "Enable this if you encounter write errors when saving to custom folders. Android 13+ restricts access to certain directories by default.", + "@allFilesAccessDescription": { + "description": "Description explaining when to enable all files access" + }, + "allFilesAccessDeniedMessage": "Permission was denied. Please enable 'All files access' manually in system settings.", + "@allFilesAccessDeniedMessage": { + "description": "Message when permission is permanently denied" + }, + "allFilesAccessDisabledMessage": "All Files Access disabled. The app will use limited storage access.", + "@allFilesAccessDisabledMessage": { + "description": "Snackbar message when user disables all files access" + }, + "settingsLocalLibrary": "Local Library", + "@settingsLocalLibrary": { + "description": "Settings menu item - local library" + }, + "settingsLocalLibrarySubtitle": "Scan music & detect duplicates", + "@settingsLocalLibrarySubtitle": { + "description": "Subtitle for local library settings" + }, + "settingsCache": "Storage & Cache", + "@settingsCache": { + "description": "Settings menu item - cache management" + }, + "settingsCacheSubtitle": "View size and clear cached data", + "@settingsCacheSubtitle": { + "description": "Subtitle for cache management menu" + }, + "libraryTitle": "Local Library", + "@libraryTitle": { + "description": "Library settings page title" + }, + "libraryScanSettings": "Scan Settings", + "@libraryScanSettings": { + "description": "Section header for scan settings" + }, + "libraryEnableLocalLibrary": "Enable Local Library", + "@libraryEnableLocalLibrary": { + "description": "Toggle to enable library scanning" + }, + "libraryEnableLocalLibrarySubtitle": "Scan and track your existing music", + "@libraryEnableLocalLibrarySubtitle": { + "description": "Subtitle for enable toggle" + }, + "libraryFolder": "Library Folder", + "@libraryFolder": { + "description": "Folder selection setting" + }, + "libraryFolderHint": "Tap to select folder", + "@libraryFolderHint": { + "description": "Placeholder when no folder selected" + }, + "libraryShowDuplicateIndicator": "Show Duplicate Indicator", + "@libraryShowDuplicateIndicator": { + "description": "Toggle for duplicate indicator in search" + }, + "libraryShowDuplicateIndicatorSubtitle": "Show when searching for existing tracks", + "@libraryShowDuplicateIndicatorSubtitle": { + "description": "Subtitle for duplicate indicator toggle" + }, + "libraryAutoScan": "Auto Scan", + "@libraryAutoScan": { + "description": "Setting for automatic library scanning" + }, + "libraryAutoScanSubtitle": "Automatically scan your library for new files", + "@libraryAutoScanSubtitle": { + "description": "Subtitle for auto scan setting" + }, + "libraryAutoScanOff": "Off", + "@libraryAutoScanOff": { + "description": "Auto scan disabled" + }, + "libraryAutoScanOnOpen": "Every app open", + "@libraryAutoScanOnOpen": { + "description": "Auto scan when app opens" + }, + "libraryAutoScanDaily": "Daily", + "@libraryAutoScanDaily": { + "description": "Auto scan once per day" + }, + "libraryAutoScanWeekly": "Weekly", + "@libraryAutoScanWeekly": { + "description": "Auto scan once per week" + }, + "libraryActions": "Actions", + "@libraryActions": { + "description": "Section header for library actions" + }, + "libraryScan": "Scan Library", + "@libraryScan": { + "description": "Button to start library scan" + }, + "libraryScanSubtitle": "Scan for audio files", + "@libraryScanSubtitle": { + "description": "Subtitle for scan button" + }, + "libraryScanSelectFolderFirst": "Select a folder first", + "@libraryScanSelectFolderFirst": { + "description": "Message when trying to scan without folder" + }, + "libraryCleanupMissingFiles": "Cleanup Missing Files", + "@libraryCleanupMissingFiles": { + "description": "Button to remove entries for missing files" + }, + "libraryCleanupMissingFilesSubtitle": "Remove entries for files that no longer exist", + "@libraryCleanupMissingFilesSubtitle": { + "description": "Subtitle for cleanup button" + }, + "libraryClear": "Clear Library", + "@libraryClear": { + "description": "Button to clear all library entries" + }, + "libraryClearSubtitle": "Remove all scanned tracks", + "@libraryClearSubtitle": { + "description": "Subtitle for clear button" + }, + "libraryClearConfirmTitle": "Clear Library", + "@libraryClearConfirmTitle": { + "description": "Dialog title for clear confirmation" + }, + "libraryClearConfirmMessage": "This will remove all scanned tracks from your library. Your actual music files will not be deleted.", + "@libraryClearConfirmMessage": { + "description": "Dialog message for clear confirmation" + }, + "libraryAbout": "About Local Library", + "@libraryAbout": { + "description": "Section header for about info" + }, + "libraryAboutDescription": "Scans your existing music collection to detect duplicates when downloading. Supports FLAC, M4A, MP3, Opus, and OGG formats. Metadata is read from file tags when available.", + "@libraryAboutDescription": { + "description": "Description of local library feature" + }, + "libraryTracksUnit": "{count, plural, =1{track} other{tracks}}", + "@libraryTracksUnit": { + "description": "Unit label for tracks count (without the number itself)", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "libraryFilesUnit": "{count, plural, =1{file} other{files}}", + "@libraryFilesUnit": { + "description": "Unit label for files count during library scanning", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "libraryLastScanned": "Last scanned: {time}", + "@libraryLastScanned": { + "description": "Last scan time display", + "placeholders": { + "time": { + "type": "String" + } + } + }, + "libraryLastScannedNever": "Never", + "@libraryLastScannedNever": { + "description": "Shown when library has never been scanned" + }, + "libraryScanning": "Scanning...", + "@libraryScanning": { + "description": "Status during scan" + }, + "libraryScanFinalizing": "Finalizing library...", + "@libraryScanFinalizing": { + "description": "Status shown after file scanning finishes but library persistence is still running" + }, + "libraryScanProgress": "{progress}% of {total} files", + "@libraryScanProgress": { + "description": "Scan progress display", + "placeholders": { + "progress": { + "type": "String" + }, + "total": { + "type": "int" + } + } + }, + "libraryInLibrary": "In Library", + "@libraryInLibrary": { + "description": "Badge shown on tracks that exist in local library" + }, + "libraryRemovedMissingFiles": "Removed {count} missing files from library", + "@libraryRemovedMissingFiles": { + "description": "Snackbar after cleanup", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "libraryCleared": "Library cleared", + "@libraryCleared": { + "description": "Snackbar after clearing library" + }, + "libraryStorageAccessRequired": "Storage Access Required", + "@libraryStorageAccessRequired": { + "description": "Dialog title for storage permission" + }, + "libraryStorageAccessMessage": "SpotiFLAC needs storage access to scan your music library. Please grant permission in settings.", + "@libraryStorageAccessMessage": { + "description": "Dialog message for storage permission" + }, + "libraryFolderNotExist": "Selected folder does not exist", + "@libraryFolderNotExist": { + "description": "Error when folder doesn't exist" + }, + "librarySourceDownloaded": "Downloaded", + "@librarySourceDownloaded": { + "description": "Badge for tracks downloaded via SpotiFLAC" + }, + "librarySourceLocal": "Local", + "@librarySourceLocal": { + "description": "Badge for tracks from local library scan" + }, + "libraryFilterAll": "All", + "@libraryFilterAll": { + "description": "Filter chip - show all library items" + }, + "libraryFilterDownloaded": "Downloaded", + "@libraryFilterDownloaded": { + "description": "Filter chip - show only downloaded items" + }, + "libraryFilterLocal": "Local", + "@libraryFilterLocal": { + "description": "Filter chip - show only local library items" + }, + "libraryFilterTitle": "Filters", + "@libraryFilterTitle": { + "description": "Filter bottom sheet title" + }, + "libraryFilterReset": "Reset", + "@libraryFilterReset": { + "description": "Reset all filters button" + }, + "libraryFilterApply": "Apply", + "@libraryFilterApply": { + "description": "Apply filters button" + }, + "libraryFilterSource": "Source", + "@libraryFilterSource": { + "description": "Filter section - source type" + }, + "libraryFilterQuality": "Quality", + "@libraryFilterQuality": { + "description": "Filter section - audio quality" + }, + "libraryFilterQualityHiRes": "Hi-Res (24bit)", + "@libraryFilterQualityHiRes": { + "description": "Filter option - high resolution audio" + }, + "libraryFilterQualityCD": "CD (16bit)", + "@libraryFilterQualityCD": { + "description": "Filter option - CD quality audio" + }, + "libraryFilterQualityLossy": "Lossy", + "@libraryFilterQualityLossy": { + "description": "Filter option - lossy compressed audio" + }, + "libraryFilterFormat": "Format", + "@libraryFilterFormat": { + "description": "Filter section - file format" + }, + "libraryFilterMetadata": "Metadata", + "@libraryFilterMetadata": { + "description": "Filter section - metadata completeness" + }, + "libraryFilterMetadataComplete": "Complete metadata", + "@libraryFilterMetadataComplete": { + "description": "Filter option - items with complete metadata" + }, + "libraryFilterMetadataMissingAny": "Missing any metadata", + "@libraryFilterMetadataMissingAny": { + "description": "Filter option - items missing any tracked metadata field" + }, + "libraryFilterMetadataMissingYear": "Missing year", + "@libraryFilterMetadataMissingYear": { + "description": "Filter option - items missing release year/date" + }, + "libraryFilterMetadataMissingGenre": "Missing genre", + "@libraryFilterMetadataMissingGenre": { + "description": "Filter option - items missing genre" + }, + "libraryFilterMetadataMissingAlbumArtist": "Missing album artist", + "@libraryFilterMetadataMissingAlbumArtist": { + "description": "Filter option - items missing album artist" + }, + "libraryFilterSort": "Sort", + "@libraryFilterSort": { + "description": "Filter section - sort order" + }, + "libraryFilterSortLatest": "Latest", + "@libraryFilterSortLatest": { + "description": "Sort option - newest first" + }, + "libraryFilterSortOldest": "Oldest", + "@libraryFilterSortOldest": { + "description": "Sort option - oldest first" + }, + "libraryFilterSortAlbumAsc": "Album (A-Z)", + "@libraryFilterSortAlbumAsc": { + "description": "Sort option - album ascending" + }, + "libraryFilterSortAlbumDesc": "Album (Z-A)", + "@libraryFilterSortAlbumDesc": { + "description": "Sort option - album descending" + }, + "libraryFilterSortGenreAsc": "Genre (A-Z)", + "@libraryFilterSortGenreAsc": { + "description": "Sort option - genre ascending" + }, + "libraryFilterSortGenreDesc": "Genre (Z-A)", + "@libraryFilterSortGenreDesc": { + "description": "Sort option - genre descending" + }, + "timeJustNow": "Just now", + "@timeJustNow": { + "description": "Relative time - less than a minute ago" + }, + "timeMinutesAgo": "{count, plural, =1{1 minute ago} other{{count} minutes ago}}", + "@timeMinutesAgo": { + "description": "Relative time - minutes ago", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "timeHoursAgo": "{count, plural, =1{1 hour ago} other{{count} hours ago}}", + "@timeHoursAgo": { + "description": "Relative time - hours ago", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "tutorialWelcomeTitle": "Welcome to SpotiFLAC!", + "@tutorialWelcomeTitle": { + "description": "Tutorial welcome page title" + }, + "tutorialWelcomeDesc": "Let's learn how to download your favorite music in lossless quality. This quick tutorial will show you the basics.", + "@tutorialWelcomeDesc": { + "description": "Tutorial welcome page description" + }, + "tutorialWelcomeTip1": "Download music from Spotify, Deezer, or paste any supported URL", + "@tutorialWelcomeTip1": { + "description": "Tutorial welcome tip 1" + }, + "tutorialWelcomeTip2": "Get FLAC quality audio from Tidal, Qobuz, or Deezer", + "@tutorialWelcomeTip2": { + "description": "Tutorial welcome tip 2" + }, + "tutorialWelcomeTip3": "Automatic metadata, cover art, and lyrics embedding", + "@tutorialWelcomeTip3": { + "description": "Tutorial welcome tip 3" + }, + "tutorialSearchTitle": "Finding Music", + "@tutorialSearchTitle": { + "description": "Tutorial search page title" + }, + "tutorialSearchDesc": "There are two easy ways to find music you want to download.", + "@tutorialSearchDesc": { + "description": "Tutorial search page description" + }, + "tutorialDownloadTitle": "Downloading Music", + "@tutorialDownloadTitle": { + "description": "Tutorial download page title" + }, + "tutorialDownloadDesc": "Downloading music is simple and fast. Here's how it works.", + "@tutorialDownloadDesc": { + "description": "Tutorial download page description" + }, + "tutorialLibraryTitle": "Your Library", + "@tutorialLibraryTitle": { + "description": "Tutorial library page title" + }, + "tutorialLibraryDesc": "All your downloaded music is organized in the Library tab.", + "@tutorialLibraryDesc": { + "description": "Tutorial library page description" + }, + "tutorialLibraryTip1": "View download progress and queue in the Library tab", + "@tutorialLibraryTip1": { + "description": "Tutorial library tip 1" + }, + "tutorialLibraryTip2": "Tap any track to play it with your music player", + "@tutorialLibraryTip2": { + "description": "Tutorial library tip 2" + }, + "tutorialLibraryTip3": "Switch between list and grid view for better browsing", + "@tutorialLibraryTip3": { + "description": "Tutorial library tip 3" + }, + "tutorialExtensionsTitle": "Extensions", + "@tutorialExtensionsTitle": { + "description": "Tutorial extensions page title" + }, + "tutorialExtensionsDesc": "Extend the app's capabilities with community extensions.", + "@tutorialExtensionsDesc": { + "description": "Tutorial extensions page description" + }, + "tutorialExtensionsTip1": "Browse the Repo tab to discover useful extensions", + "@tutorialExtensionsTip1": { + "description": "Tutorial extensions tip 1" + }, + "tutorialExtensionsTip2": "Add new download providers or search sources", + "@tutorialExtensionsTip2": { + "description": "Tutorial extensions tip 2" + }, + "tutorialExtensionsTip3": "Get lyrics, enhanced metadata, and more features", + "@tutorialExtensionsTip3": { + "description": "Tutorial extensions tip 3" + }, + "tutorialSettingsTitle": "Customize Your Experience", + "@tutorialSettingsTitle": { + "description": "Tutorial settings page title" + }, + "tutorialSettingsDesc": "Personalize the app in Settings to match your preferences.", + "@tutorialSettingsDesc": { + "description": "Tutorial settings page description" + }, + "tutorialSettingsTip1": "Change download location and folder organization", + "@tutorialSettingsTip1": { + "description": "Tutorial settings tip 1" + }, + "tutorialSettingsTip2": "Set default audio quality and format preferences", + "@tutorialSettingsTip2": { + "description": "Tutorial settings tip 2" + }, + "tutorialSettingsTip3": "Customize app theme and appearance", + "@tutorialSettingsTip3": { + "description": "Tutorial settings tip 3" + }, + "tutorialReadyMessage": "You're all set! Start downloading your favorite music now.", + "@tutorialReadyMessage": { + "description": "Tutorial completion message" + }, + "libraryForceFullScan": "Force Full Scan", + "@libraryForceFullScan": { + "description": "Button to force a complete rescan of library" + }, + "libraryForceFullScanSubtitle": "Rescan all files, ignoring cache", + "@libraryForceFullScanSubtitle": { + "description": "Subtitle for force full scan button" + }, + "cleanupOrphanedDownloads": "Cleanup Orphaned Downloads", + "@cleanupOrphanedDownloads": { + "description": "Button to remove history entries for deleted files" + }, + "cleanupOrphanedDownloadsSubtitle": "Remove history entries for files that no longer exist", + "@cleanupOrphanedDownloadsSubtitle": { + "description": "Subtitle for orphaned cleanup button" + }, + "cleanupOrphanedDownloadsResult": "Removed {count} orphaned entries from history", + "@cleanupOrphanedDownloadsResult": { + "description": "Snackbar after orphan cleanup", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "cleanupOrphanedDownloadsNone": "No orphaned entries found", + "@cleanupOrphanedDownloadsNone": { + "description": "Snackbar when no orphans found" + }, + "cacheTitle": "Storage & Cache", + "@cacheTitle": { + "description": "Cache management page title" + }, + "cacheSummaryTitle": "Cache overview", + "@cacheSummaryTitle": { + "description": "Heading for cache summary card" + }, + "cacheSummarySubtitle": "Clearing cache will not remove downloaded music files.", + "@cacheSummarySubtitle": { + "description": "Helper text for cache summary card" + }, + "cacheEstimatedTotal": "Estimated cache usage: {size}", + "@cacheEstimatedTotal": { + "description": "Total cache size shown in summary", + "placeholders": { + "size": { + "type": "String" + } + } + }, + "cacheSectionStorage": "Cached Data", + "@cacheSectionStorage": { + "description": "Section header for cache entries" + }, + "cacheSectionMaintenance": "Maintenance", + "@cacheSectionMaintenance": { + "description": "Section header for cleanup actions" + }, + "cacheAppDirectory": "App cache directory", + "@cacheAppDirectory": { + "description": "Cache item title for app cache directory" + }, + "cacheAppDirectoryDesc": "HTTP responses, WebView data, and other temporary app data.", + "@cacheAppDirectoryDesc": { + "description": "Description of what app cache directory contains" + }, + "cacheTempDirectory": "Temporary directory", + "@cacheTempDirectory": { + "description": "Cache item title for temporary files directory" + }, + "cacheTempDirectoryDesc": "Temporary files from downloads and audio conversion.", + "@cacheTempDirectoryDesc": { + "description": "Description of what temporary directory contains" + }, + "cacheCoverImage": "Cover image cache", + "@cacheCoverImage": { + "description": "Cache item title for persistent cover images" + }, + "cacheCoverImageDesc": "Downloaded album and track cover art. Will re-download when viewed.", + "@cacheCoverImageDesc": { + "description": "Description of what cover image cache contains" + }, + "cacheLibraryCover": "Library cover cache", + "@cacheLibraryCover": { + "description": "Cache item title for local library cover art images" + }, + "cacheLibraryCoverDesc": "Cover art extracted from local music files. Will re-extract on next scan.", + "@cacheLibraryCoverDesc": { + "description": "Description of what library cover cache contains" + }, + "cacheExploreFeed": "Explore feed cache", + "@cacheExploreFeed": { + "description": "Cache item title for explore home feed cache" + }, + "cacheExploreFeedDesc": "Explore tab content (new releases, trending). Will refresh on next visit.", + "@cacheExploreFeedDesc": { + "description": "Description of what explore feed cache contains" + }, + "cacheTrackLookup": "Track lookup cache", + "@cacheTrackLookup": { + "description": "Cache item title for track ID lookup cache" + }, + "cacheTrackLookupDesc": "Spotify/Deezer track ID lookups. Clearing may slow next few searches.", + "@cacheTrackLookupDesc": { + "description": "Description of what track lookup cache contains" + }, + "cacheCleanupUnusedDesc": "Remove orphaned download history and library entries for missing files.", + "@cacheCleanupUnusedDesc": { + "description": "Description of what cleanup unused data does" + }, + "cacheNoData": "No cached data", + "@cacheNoData": { + "description": "Label when cache category has no data" + }, + "cacheSizeWithFiles": "{size} in {count} files", + "@cacheSizeWithFiles": { + "description": "Cache size and file count", + "placeholders": { + "size": { + "type": "String" + }, + "count": { + "type": "int" + } + } + }, + "cacheSizeOnly": "{size}", + "@cacheSizeOnly": { + "description": "Cache size only", + "placeholders": { + "size": { + "type": "String" + } + } + }, + "cacheEntries": "{count} entries", + "@cacheEntries": { + "description": "Track cache entry count", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "cacheClearSuccess": "Cleared: {target}", + "@cacheClearSuccess": { + "description": "Snackbar after clearing selected cache", + "placeholders": { + "target": { + "type": "String" + } + } + }, + "cacheClearConfirmTitle": "Clear cache?", + "@cacheClearConfirmTitle": { + "description": "Dialog title before clearing one cache category" + }, + "cacheClearConfirmMessage": "This will clear cached data for {target}. Downloaded music files will not be deleted.", + "@cacheClearConfirmMessage": { + "description": "Dialog message before clearing selected cache", + "placeholders": { + "target": { + "type": "String" + } + } + }, + "cacheClearAllConfirmTitle": "Clear all cache?", + "@cacheClearAllConfirmTitle": { + "description": "Dialog title before clearing all caches" + }, + "cacheClearAllConfirmMessage": "This will clear all cache categories on this page. Downloaded music files will not be deleted.", + "@cacheClearAllConfirmMessage": { + "description": "Dialog message before clearing all caches" + }, + "cacheClearAll": "Clear all cache", + "@cacheClearAll": { + "description": "Button label to clear all caches" + }, + "cacheCleanupUnused": "Cleanup unused data", + "@cacheCleanupUnused": { + "description": "Action title for cleaning unused entries" + }, + "cacheCleanupUnusedSubtitle": "Remove orphaned download history and missing library entries", + "@cacheCleanupUnusedSubtitle": { + "description": "Subtitle for cleanup unused data action" + }, + "cacheCleanupResult": "Cleanup completed: {downloadCount} orphaned downloads, {libraryCount} missing library entries", + "@cacheCleanupResult": { + "description": "Snackbar after unused data cleanup", + "placeholders": { + "downloadCount": { + "type": "int" + }, + "libraryCount": { + "type": "int" + } + } + }, + "cacheRefreshStats": "Refresh stats", + "@cacheRefreshStats": { + "description": "Button label to refresh cache statistics" + }, + "trackSaveCoverArt": "Save Cover Art", + "@trackSaveCoverArt": { + "description": "Menu action - save album cover art as file" + }, + "trackSaveCoverArtSubtitle": "Save album art as .jpg file", + "@trackSaveCoverArtSubtitle": { + "description": "Subtitle for save cover art action" + }, + "trackSaveLyrics": "Save Lyrics (.lrc)", + "@trackSaveLyrics": { + "description": "Menu action - save lyrics as .lrc file" + }, + "trackSaveLyricsSubtitle": "Fetch and save lyrics as .lrc file", + "@trackSaveLyricsSubtitle": { + "description": "Subtitle for save lyrics action" + }, + "trackSaveLyricsProgress": "Saving lyrics...", + "@trackSaveLyricsProgress": { + "description": "Snackbar while saving lyrics to file" + }, + "trackReEnrich": "Re-enrich", + "@trackReEnrich": { + "description": "Menu action - re-embed metadata into audio file" + }, + "trackReEnrichOnlineSubtitle": "Search metadata online and embed into file", + "@trackReEnrichOnlineSubtitle": { + "description": "Subtitle for re-enrich metadata action for local items" + }, + "trackReEnrichFieldsTitle": "Fields to update", + "@trackReEnrichFieldsTitle": { + "description": "Section title for field selection in re-enrich dialog" + }, + "trackReEnrichFieldCover": "Cover Art", + "@trackReEnrichFieldCover": { + "description": "Checkbox label for cover art field in re-enrich" + }, + "trackReEnrichFieldLyrics": "Lyrics", + "@trackReEnrichFieldLyrics": { + "description": "Checkbox label for lyrics field in re-enrich" + }, + "trackReEnrichFieldBasicTags": "Album, Album Artist", + "@trackReEnrichFieldBasicTags": { + "description": "Checkbox label for basic tags in re-enrich (title/artist are never overwritten)" + }, + "trackReEnrichFieldTrackInfo": "Track & Disc Number", + "@trackReEnrichFieldTrackInfo": { + "description": "Checkbox label for track info in re-enrich" + }, + "trackReEnrichFieldReleaseInfo": "Date & ISRC", + "@trackReEnrichFieldReleaseInfo": { + "description": "Checkbox label for release info in re-enrich" + }, + "trackReEnrichFieldExtra": "Genre, Label, Copyright", + "@trackReEnrichFieldExtra": { + "description": "Checkbox label for extra metadata in re-enrich" + }, + "trackReEnrichSelectAll": "Select All", + "@trackReEnrichSelectAll": { + "description": "Select all fields checkbox in re-enrich" + }, + "trackEditMetadata": "Edit Metadata", + "@trackEditMetadata": { + "description": "Menu action - edit embedded metadata" + }, + "trackCoverSaved": "Cover art saved to {fileName}", + "@trackCoverSaved": { + "description": "Snackbar after cover art saved", + "placeholders": { + "fileName": { + "type": "String" + } + } + }, + "trackCoverNoSource": "No cover art source available", + "@trackCoverNoSource": { + "description": "Snackbar when no cover art URL or embedded cover" + }, + "trackLyricsSaved": "Lyrics saved to {fileName}", + "@trackLyricsSaved": { + "description": "Snackbar after lyrics saved", + "placeholders": { + "fileName": { + "type": "String" + } + } + }, + "trackReEnrichProgress": "Re-enriching metadata...", + "@trackReEnrichProgress": { + "description": "Snackbar while re-enriching metadata" + }, + "trackReEnrichSearching": "Searching metadata online...", + "@trackReEnrichSearching": { + "description": "Snackbar while searching metadata from internet for local items" + }, + "trackReEnrichSuccess": "Metadata re-enriched successfully", + "@trackReEnrichSuccess": { + "description": "Snackbar after successful re-enrichment" + }, + "trackReEnrichFfmpegFailed": "FFmpeg metadata embed failed", + "@trackReEnrichFfmpegFailed": { + "description": "Snackbar when FFmpeg embed fails for MP3/Opus" + }, + "queueFlacAction": "Queue FLAC", + "@queueFlacAction": { + "description": "Action/button label for queueing FLAC redownloads for local tracks" + }, + "queueFlacConfirmMessage": "Search online matches for the selected tracks and queue FLAC downloads.\n\nExisting files will not be modified or deleted.\n\nOnly high-confidence matches are queued automatically.\n\n{count} selected", + "@queueFlacConfirmMessage": { + "description": "Confirmation dialog body before queueing FLAC redownloads for local tracks", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "queueFlacFindingProgress": "Finding FLAC matches... ({current}/{total})", + "@queueFlacFindingProgress": { + "description": "Snackbar while resolving remote matches for local FLAC redownloads", + "placeholders": { + "current": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "queueFlacNoReliableMatches": "No reliable online matches found for the selection", + "@queueFlacNoReliableMatches": { + "description": "Snackbar when no safe FLAC redownload matches were found" + }, + "queueFlacQueuedWithSkipped": "Added {addedCount} tracks to queue, skipped {skippedCount}", + "@queueFlacQueuedWithSkipped": { + "description": "Snackbar when some selected local tracks were queued for FLAC redownload and some were skipped", + "placeholders": { + "addedCount": { + "type": "int" + }, + "skippedCount": { + "type": "int" + } + } + }, + "trackSaveFailed": "Failed: {error}", + "@trackSaveFailed": { + "description": "Snackbar when save operation fails", + "placeholders": { + "error": { + "type": "String" + } + } + }, + "trackConvertFormat": "Convert Format", + "@trackConvertFormat": { + "description": "Menu item - convert audio format" + }, + "trackConvertFormatSubtitle": "Convert to MP3, Opus, ALAC, or FLAC", + "@trackConvertFormatSubtitle": { + "description": "Subtitle for convert format menu item" + }, + "trackConvertTitle": "Convert Audio", + "@trackConvertTitle": { + "description": "Title of convert bottom sheet" + }, + "trackConvertTargetFormat": "Target Format", + "@trackConvertTargetFormat": { + "description": "Label for format selection" + }, + "trackConvertBitrate": "Bitrate", + "@trackConvertBitrate": { + "description": "Label for bitrate selection" + }, + "trackConvertConfirmTitle": "Confirm Conversion", + "@trackConvertConfirmTitle": { + "description": "Confirmation dialog title" + }, + "trackConvertConfirmMessage": "Convert from {sourceFormat} to {targetFormat} at {bitrate}?\n\nThe original file will be deleted after conversion.", + "@trackConvertConfirmMessage": { + "description": "Confirmation dialog message", + "placeholders": { + "sourceFormat": { + "type": "String" + }, + "targetFormat": { + "type": "String" + }, + "bitrate": { + "type": "String" + } + } + }, + "trackConvertConfirmMessageLossless": "Convert from {sourceFormat} to {targetFormat}? (Lossless — no quality loss)\n\nThe original file will be deleted after conversion.", + "@trackConvertConfirmMessageLossless": { + "description": "Confirmation dialog message for lossless-to-lossless conversion", + "placeholders": { + "sourceFormat": { + "type": "String" + }, + "targetFormat": { + "type": "String" + } + } + }, + "trackConvertLosslessHint": "Lossless conversion — no quality loss", + "@trackConvertLosslessHint": { + "description": "Hint shown when converting between lossless formats" + }, + "trackConvertConverting": "Converting audio...", + "@trackConvertConverting": { + "description": "Snackbar while converting" + }, + "trackConvertSuccess": "Converted to {format} successfully", + "@trackConvertSuccess": { + "description": "Snackbar after successful conversion", + "placeholders": { + "format": { + "type": "String" + } + } + }, + "trackConvertFailed": "Conversion failed", + "@trackConvertFailed": { + "description": "Snackbar when conversion fails" + }, + "cueSplitTitle": "Split CUE Sheet", + "@cueSplitTitle": { + "description": "Title for CUE split bottom sheet" + }, + "cueSplitSubtitle": "Split CUE+FLAC into individual tracks", + "@cueSplitSubtitle": { + "description": "Subtitle for CUE split menu item" + }, + "cueSplitAlbum": "Album: {album}", + "@cueSplitAlbum": { + "description": "Album name in CUE split sheet", + "placeholders": { + "album": { + "type": "String" + } + } + }, + "cueSplitArtist": "Artist: {artist}", + "@cueSplitArtist": { + "description": "Artist name in CUE split sheet", + "placeholders": { + "artist": { + "type": "String" + } + } + }, + "cueSplitTrackCount": "{count} tracks", + "@cueSplitTrackCount": { + "description": "Number of tracks in CUE sheet", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "cueSplitConfirmTitle": "Split CUE Album", + "@cueSplitConfirmTitle": { + "description": "CUE split confirmation dialog title" + }, + "cueSplitConfirmMessage": "Split \"{album}\" into {count} individual FLAC files?\n\nFiles will be saved to the same directory.", + "@cueSplitConfirmMessage": { + "description": "CUE split confirmation dialog message", + "placeholders": { + "album": { + "type": "String" + }, + "count": { + "type": "int" + } + } + }, + "cueSplitSplitting": "Splitting CUE sheet... ({current}/{total})", + "@cueSplitSplitting": { + "description": "Snackbar while splitting CUE", + "placeholders": { + "current": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "cueSplitSuccess": "Split into {count} tracks successfully", + "@cueSplitSuccess": { + "description": "Snackbar after successful CUE split", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "cueSplitFailed": "CUE split failed", + "@cueSplitFailed": { + "description": "Snackbar when CUE split fails" + }, + "cueSplitNoAudioFile": "Audio file not found for this CUE sheet", + "@cueSplitNoAudioFile": { + "description": "Error when CUE audio file is missing" + }, + "cueSplitButton": "Split into Tracks", + "@cueSplitButton": { + "description": "Button text to start CUE splitting" + }, + "actionCreate": "Create", + "@actionCreate": { + "description": "Generic action button - create" + }, + "collectionFoldersTitle": "My folders", + "@collectionFoldersTitle": { + "description": "Library section title for custom folders" + }, + "collectionWishlist": "Wishlist", + "@collectionWishlist": { + "description": "Custom folder for saved tracks to download later" + }, + "collectionLoved": "Loved", + "@collectionLoved": { + "description": "Custom folder for favorite tracks" + }, + "collectionPlaylists": "Playlists", + "@collectionPlaylists": { + "description": "Custom user playlists folder" + }, + "collectionPlaylist": "Playlist", + "@collectionPlaylist": { + "description": "Single playlist label" + }, + "collectionAddToPlaylist": "Add to playlist", + "@collectionAddToPlaylist": { + "description": "Action to add a track to user playlist" + }, + "collectionCreatePlaylist": "Create playlist", + "@collectionCreatePlaylist": { + "description": "Action to create a new playlist" + }, + "collectionNoPlaylistsYet": "No playlists yet", + "@collectionNoPlaylistsYet": { + "description": "Empty state title when user has no playlists" + }, + "collectionNoPlaylistsSubtitle": "Create a playlist to start categorizing tracks", + "@collectionNoPlaylistsSubtitle": { + "description": "Empty state subtitle when user has no playlists" + }, + "collectionPlaylistTracks": "{count, plural, =1{1 track} other{{count} tracks}}", + "@collectionPlaylistTracks": { + "description": "Track count label for custom playlists", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "collectionAddedToPlaylist": "Added to \"{playlistName}\"", + "@collectionAddedToPlaylist": { + "description": "Snackbar after adding track to playlist", + "placeholders": { + "playlistName": { + "type": "String" + } + } + }, + "collectionAlreadyInPlaylist": "Already in \"{playlistName}\"", + "@collectionAlreadyInPlaylist": { + "description": "Snackbar when track already exists in playlist", + "placeholders": { + "playlistName": { + "type": "String" + } + } + }, + "collectionPlaylistCreated": "Playlist created", + "@collectionPlaylistCreated": { + "description": "Snackbar after creating playlist" + }, + "collectionPlaylistNameHint": "Playlist name", + "@collectionPlaylistNameHint": { + "description": "Hint text for playlist name input" + }, + "collectionPlaylistNameRequired": "Playlist name is required", + "@collectionPlaylistNameRequired": { + "description": "Validation error for empty playlist name" + }, + "collectionRenamePlaylist": "Rename playlist", + "@collectionRenamePlaylist": { + "description": "Action to rename playlist" + }, + "collectionDeletePlaylist": "Delete playlist", + "@collectionDeletePlaylist": { + "description": "Action to delete playlist" + }, + "collectionDeletePlaylistMessage": "Delete \"{playlistName}\" and all tracks inside it?", + "@collectionDeletePlaylistMessage": { + "description": "Confirmation message for deleting playlist", + "placeholders": { + "playlistName": { + "type": "String" + } + } + }, + "collectionPlaylistDeleted": "Playlist deleted", + "@collectionPlaylistDeleted": { + "description": "Snackbar after deleting playlist" + }, + "collectionPlaylistRenamed": "Playlist renamed", + "@collectionPlaylistRenamed": { + "description": "Snackbar after renaming playlist" + }, + "collectionWishlistEmptyTitle": "Wishlist is empty", + "@collectionWishlistEmptyTitle": { + "description": "Wishlist empty state title" + }, + "collectionWishlistEmptySubtitle": "Tap + on tracks to save what you want to download later", + "@collectionWishlistEmptySubtitle": { + "description": "Wishlist empty state subtitle" + }, + "collectionLovedEmptyTitle": "Loved folder is empty", + "@collectionLovedEmptyTitle": { + "description": "Loved empty state title" + }, + "collectionLovedEmptySubtitle": "Tap love on tracks to keep your favorites", + "@collectionLovedEmptySubtitle": { + "description": "Loved empty state subtitle" + }, + "collectionPlaylistEmptyTitle": "Playlist is empty", + "@collectionPlaylistEmptyTitle": { + "description": "Playlist empty state title" + }, + "collectionPlaylistEmptySubtitle": "Long-press + on any track to add it here", + "@collectionPlaylistEmptySubtitle": { + "description": "Playlist empty state subtitle" + }, + "collectionRemoveFromPlaylist": "Remove from playlist", + "@collectionRemoveFromPlaylist": { + "description": "Tooltip for removing track from playlist" + }, + "collectionRemoveFromFolder": "Remove from folder", + "@collectionRemoveFromFolder": { + "description": "Tooltip for removing track from wishlist/loved folder" + }, + "collectionRemoved": "\"{trackName}\" removed", + "@collectionRemoved": { + "description": "Snackbar after removing a track from a collection", + "placeholders": { + "trackName": { + "type": "String" + } + } + }, + "collectionAddedToLoved": "\"{trackName}\" added to Loved", + "@collectionAddedToLoved": { + "description": "Snackbar after adding track to loved folder", + "placeholders": { + "trackName": { + "type": "String" + } + } + }, + "collectionRemovedFromLoved": "\"{trackName}\" removed from Loved", + "@collectionRemovedFromLoved": { + "description": "Snackbar after removing track from loved folder", + "placeholders": { + "trackName": { + "type": "String" + } + } + }, + "collectionAddedToWishlist": "\"{trackName}\" added to Wishlist", + "@collectionAddedToWishlist": { + "description": "Snackbar after adding track to wishlist", + "placeholders": { + "trackName": { + "type": "String" + } + } + }, + "collectionRemovedFromWishlist": "\"{trackName}\" removed from Wishlist", + "@collectionRemovedFromWishlist": { + "description": "Snackbar after removing track from wishlist", + "placeholders": { + "trackName": { + "type": "String" + } + } + }, + "trackOptionAddToLoved": "Add to Loved", + "@trackOptionAddToLoved": { + "description": "Bottom sheet action label - add track to loved folder" + }, + "trackOptionRemoveFromLoved": "Remove from Loved", + "@trackOptionRemoveFromLoved": { + "description": "Bottom sheet action label - remove track from loved folder" + }, + "trackOptionAddToWishlist": "Add to Wishlist", + "@trackOptionAddToWishlist": { + "description": "Bottom sheet action label - add track to wishlist" + }, + "trackOptionRemoveFromWishlist": "Remove from Wishlist", + "@trackOptionRemoveFromWishlist": { + "description": "Bottom sheet action label - remove track from wishlist" + }, + "collectionPlaylistChangeCover": "Change cover image", + "@collectionPlaylistChangeCover": { + "description": "Bottom sheet action to pick a custom cover image for a playlist" + }, + "collectionPlaylistRemoveCover": "Remove cover image", + "@collectionPlaylistRemoveCover": { + "description": "Bottom sheet action to remove custom cover image from a playlist" + }, + "selectionShareCount": "Share {count} {count, plural, =1{track} other{tracks}}", + "@selectionShareCount": { + "description": "Share button text with count in selection mode", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "selectionShareNoFiles": "No shareable files found", + "@selectionShareNoFiles": { + "description": "Snackbar when no selected files exist on disk" + }, + "selectionConvertCount": "Convert {count} {count, plural, =1{track} other{tracks}}", + "@selectionConvertCount": { + "description": "Convert button text with count in selection mode", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "selectionConvertNoConvertible": "No convertible tracks selected", + "@selectionConvertNoConvertible": { + "description": "Snackbar when no selected tracks support conversion" + }, + "selectionBatchConvertConfirmTitle": "Batch Convert", + "@selectionBatchConvertConfirmTitle": { + "description": "Confirmation dialog title for batch conversion" + }, + "selectionBatchConvertConfirmMessage": "Convert {count} {count, plural, =1{track} other{tracks}} to {format} at {bitrate}?\n\nOriginal files will be deleted after conversion.", + "@selectionBatchConvertConfirmMessage": { + "description": "Confirmation dialog message for batch conversion", + "placeholders": { + "count": { + "type": "int" + }, + "format": { + "type": "String" + }, + "bitrate": { + "type": "String" + } + } + }, + "selectionBatchConvertConfirmMessageLossless": "Convert {count} {count, plural, =1{track} other{tracks}} to {format}? (Lossless — no quality loss)\n\nOriginal files will be deleted after conversion.", + "@selectionBatchConvertConfirmMessageLossless": { + "description": "Confirmation dialog message for lossless batch conversion", + "placeholders": { + "count": { + "type": "int" + }, + "format": { + "type": "String" + } + } + }, + "selectionBatchConvertProgress": "Converting {current} of {total}...", + "@selectionBatchConvertProgress": { + "description": "Snackbar during batch conversion progress", + "placeholders": { + "current": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "selectionBatchConvertSuccess": "Converted {success} of {total} tracks to {format}", + "@selectionBatchConvertSuccess": { + "description": "Snackbar after batch conversion completes", + "placeholders": { + "success": { + "type": "int" + }, + "total": { + "type": "int" + }, + "format": { + "type": "String" + } + } + }, + "downloadUseAlbumArtistForFoldersAlbumSubtitle": "Folder named after Album Artist tag", + "@downloadUseAlbumArtistForFoldersAlbumSubtitle": { + "description": "Subtitle when album artist is used for folder names" + }, + "downloadUseAlbumArtistForFoldersTrackSubtitle": "Folder named after Track Artist tag", + "@downloadUseAlbumArtistForFoldersTrackSubtitle": { + "description": "Subtitle when track artist is used for folder names" + }, + "lyricsProvidersTitle": "Lyrics Provider Priority", + "@lyricsProvidersTitle": { + "description": "Settings item title for lyrics provider order" + }, + "lyricsProvidersDescription": "Enable, disable and reorder lyrics sources. Providers are tried top-to-bottom until lyrics are found.", + "@lyricsProvidersDescription": { + "description": "Description on the lyrics provider priority page" + }, + "lyricsProvidersInfoText": "Extension lyrics providers always run before built-in providers. At least one provider must remain enabled.", + "@lyricsProvidersInfoText": { + "description": "Info tip on lyrics provider priority page" + }, + "lyricsProvidersEnabledSection": "Enabled ({count})", + "@lyricsProvidersEnabledSection": { + "description": "Section header for enabled providers", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "lyricsProvidersDisabledSection": "Disabled ({count})", + "@lyricsProvidersDisabledSection": { + "description": "Section header for disabled providers", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "lyricsProvidersAtLeastOne": "At least one provider must remain enabled", + "@lyricsProvidersAtLeastOne": { + "description": "Snackbar when user tries to disable the last enabled provider" + }, + "lyricsProvidersSaved": "Lyrics provider priority saved", + "@lyricsProvidersSaved": { + "description": "Snackbar after saving lyrics provider priority" + }, + "lyricsProvidersDiscardContent": "You have unsaved changes that will be lost.", + "@lyricsProvidersDiscardContent": { + "description": "Body text of the discard-changes dialog on lyrics provider page" + }, + "lyricsProviderLrclibDesc": "Open-source synced lyrics database", + "@lyricsProviderLrclibDesc": { + "description": "Description for LRCLIB provider" + }, + "lyricsProviderNeteaseDesc": "NetEase Cloud Music (good for Asian songs)", + "@lyricsProviderNeteaseDesc": { + "description": "Description for Netease provider" + }, + "lyricsProviderMusixmatchDesc": "Largest lyrics database (multi-language)", + "@lyricsProviderMusixmatchDesc": { + "description": "Description for Musixmatch provider" + }, + "lyricsProviderAppleMusicDesc": "Word-by-word synced lyrics (via proxy)", + "@lyricsProviderAppleMusicDesc": { + "description": "Description for Apple Music provider" + }, + "lyricsProviderQqMusicDesc": "QQ Music (good for Chinese songs, via proxy)", + "@lyricsProviderQqMusicDesc": { + "description": "Description for QQ Music provider" + }, + "lyricsProviderExtensionDesc": "Extension provider", + "@lyricsProviderExtensionDesc": { + "description": "Generic description for extension-based lyrics providers" + }, + "safMigrationTitle": "Storage Update Required", + "@safMigrationTitle": { + "description": "Title of SAF migration dialog" + }, + "safMigrationMessage1": "SpotiFLAC now uses Android Storage Access Framework (SAF) for downloads. This fixes \"permission denied\" errors on Android 10+.", + "@safMigrationMessage1": { + "description": "First paragraph of SAF migration dialog" + }, + "safMigrationMessage2": "Please select your download folder again to switch to the new storage system.", + "@safMigrationMessage2": { + "description": "Second paragraph of SAF migration dialog" + }, + "safMigrationSuccess": "Download folder updated to SAF mode", + "@safMigrationSuccess": { + "description": "Snackbar after successfully migrating to SAF" + }, + "settingsDonate": "Support Development", + "@settingsDonate": { + "description": "Settings menu item - donate page" + }, + "settingsDonateSubtitle": "Buy the developer a coffee", + "@settingsDonateSubtitle": { + "description": "Subtitle for donate menu item" + }, + "tooltipLoveAll": "Love All", + "@tooltipLoveAll": { + "description": "Tooltip for the Love All button on album/playlist screens" + }, + "tooltipAddToPlaylist": "Add to Playlist", + "@tooltipAddToPlaylist": { + "description": "Tooltip for the Add to Playlist button" + }, + "snackbarRemovedTracksFromLoved": "Removed {count} tracks from Loved", + "@snackbarRemovedTracksFromLoved": { + "description": "Snackbar after removing multiple tracks from Loved folder", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "snackbarAddedTracksToLoved": "Added {count} tracks to Loved", + "@snackbarAddedTracksToLoved": { + "description": "Snackbar after adding multiple tracks to Loved folder", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "dialogDownloadAllTitle": "Download All", + "@dialogDownloadAllTitle": { + "description": "Dialog title for bulk download confirmation" + }, + "dialogDownloadAllMessage": "Download {count} tracks?", + "@dialogDownloadAllMessage": { + "description": "Body of the Download All confirmation dialog", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "homeSkipAlreadyDownloaded": "Skip already downloaded songs", + "@homeSkipAlreadyDownloaded": { + "description": "Checkbox label in import dialog to skip already-downloaded songs" + }, + "homeGoToAlbum": "Go to Album", + "@homeGoToAlbum": { + "description": "Context menu item to navigate to the album page" + }, + "homeAlbumInfoUnavailable": "Album info not available", + "@homeAlbumInfoUnavailable": { + "description": "Snackbar when album info cannot be loaded" + }, + "snackbarLoadingCueSheet": "Loading CUE sheet...", + "@snackbarLoadingCueSheet": { + "description": "Snackbar while loading a CUE sheet file" + }, + "snackbarMetadataSaved": "Metadata saved successfully", + "@snackbarMetadataSaved": { + "description": "Snackbar after successfully saving track metadata" + }, + "snackbarFailedToEmbedLyrics": "Failed to embed lyrics", + "@snackbarFailedToEmbedLyrics": { + "description": "Snackbar when lyrics embedding fails" + }, + "snackbarFailedToWriteStorage": "Failed to write back to storage", + "@snackbarFailedToWriteStorage": { + "description": "Snackbar when writing metadata back to file fails" + }, + "snackbarError": "Error: {error}", + "@snackbarError": { + "description": "Generic error snackbar with error detail", + "placeholders": { + "error": { + "type": "String" + } + } + }, + "snackbarNoActionDefined": "No action defined for this button", + "@snackbarNoActionDefined": { + "description": "Snackbar when an extension button has no action configured" + }, + "noTracksFoundForAlbum": "No tracks found for this album", + "@noTracksFoundForAlbum": { + "description": "Empty state message when an album has no tracks" + }, + "downloadLocationSubtitle": "Choose where to save your downloaded tracks", + "@downloadLocationSubtitle": { + "description": "Subtitle shown in the download location picker sheet" + }, + "storageModeAppFolder": "App Folder (Recommended)", + "@storageModeAppFolder": { + "description": "Storage mode option - app-managed folder" + }, + "storageModeAppFolderSubtitle": "Saves to Music/SpotiFLAC by default", + "@storageModeAppFolderSubtitle": { + "description": "Subtitle for app folder storage mode" + }, + "storageModeSaf": "Custom Folder (SAF)", + "@storageModeSaf": { + "description": "Storage mode option - Storage Access Framework" + }, + "storageModeSafSubtitle": "Pick any folder, including SD card", + "@storageModeSafSubtitle": { + "description": "Subtitle for SAF storage mode" + }, + "downloadFilenameDescription": "Use {artist}, {title}, {album}, {track}, {year}, {date}, {disc} as placeholders.", + "@downloadFilenameDescription": { + "description": "Description shown in filename format editor" + }, + "downloadFilenameInsertTag": "Tap to insert tag:", + "@downloadFilenameInsertTag": { + "description": "Label above filename tag chips" + }, + "downloadSeparateSinglesEnabled": "Singles and EPs saved in a separate folder", + "@downloadSeparateSinglesEnabled": { + "description": "Subtitle when separate singles folder is on" + }, + "downloadSeparateSinglesDisabled": "Singles and albums saved in the same folder", + "@downloadSeparateSinglesDisabled": { + "description": "Subtitle when separate singles folder is off" + }, + "downloadArtistNameFilters": "Artist Name Filters", + "@downloadArtistNameFilters": { + "description": "Setting title for artist folder filter options" + }, + "downloadCreatePlaylistSourceFolder": "Playlist Source Folder", + "@downloadCreatePlaylistSourceFolder": { + "description": "Setting to create a subfolder per playlist source" + }, + "downloadCreatePlaylistSourceFolderEnabled": "A subfolder is created for each playlist", + "@downloadCreatePlaylistSourceFolderEnabled": { + "description": "Subtitle when playlist folder is enabled" + }, + "downloadCreatePlaylistSourceFolderDisabled": "All tracks saved directly to download folder", + "@downloadCreatePlaylistSourceFolderDisabled": { + "description": "Subtitle when playlist folder is disabled" + }, + "downloadCreatePlaylistSourceFolderRedundant": "Handled by folder organization setting", + "@downloadCreatePlaylistSourceFolderRedundant": { + "description": "Subtitle when folder organization is already set to playlist" + }, + "downloadSongLinkRegion": "SongLink Region", + "@downloadSongLinkRegion": { + "description": "Setting for SongLink region used during fallback resolution" + }, + "downloadNetworkCompatibilityMode": "Network Compatibility Mode", + "@downloadNetworkCompatibilityMode": { + "description": "Setting for legacy TLS/network handling" + }, + "downloadNetworkCompatibilityModeEnabled": "Using legacy TLS settings for older networks", + "@downloadNetworkCompatibilityModeEnabled": { + "description": "Subtitle when network compatibility mode is on" + }, + "downloadNetworkCompatibilityModeDisabled": "Using standard network settings", + "@downloadNetworkCompatibilityModeDisabled": { + "description": "Subtitle when network compatibility mode is off" + }, + "downloadSelectServiceToEnable": "Select Tidal or Qobuz to enable this option", + "@downloadSelectServiceToEnable": { + "description": "Subtitle when quality picker is disabled due to extension service" + }, + "downloadSelectTidalQobuz": "Select Tidal or Qobuz to choose audio quality", + "@downloadSelectTidalQobuz": { + "description": "Info shown when a non-built-in service is selected" + }, + "downloadEmbedLyricsDisabled": "Enable metadata embedding first", + "@downloadEmbedLyricsDisabled": { + "description": "Subtitle when lyrics embedding is blocked by metadata toggle" + }, + "downloadNeteaseIncludeTranslation": "Netease: Include Translation", + "@downloadNeteaseIncludeTranslation": { + "description": "Setting to include translated lyrics from Netease" + }, + "downloadNeteaseIncludeTranslationEnabled": "Chinese translation lines included", + "@downloadNeteaseIncludeTranslationEnabled": { + "description": "Subtitle when Netease translation is on" + }, + "downloadNeteaseIncludeTranslationDisabled": "Original lyrics only", + "@downloadNeteaseIncludeTranslationDisabled": { + "description": "Subtitle when Netease translation is off" + }, + "downloadNeteaseIncludeRomanization": "Netease: Include Romanization", + "@downloadNeteaseIncludeRomanization": { + "description": "Setting to include romanized lyrics from Netease" + }, + "downloadNeteaseIncludeRomanizationEnabled": "Romanization lines included", + "@downloadNeteaseIncludeRomanizationEnabled": { + "description": "Subtitle when Netease romanization is on" + }, + "downloadNeteaseIncludeRomanizationDisabled": "No romanization", + "@downloadNeteaseIncludeRomanizationDisabled": { + "description": "Subtitle when Netease romanization is off" + }, + "downloadAppleQqMultiPerson": "Apple / QQ: Multi-Person Lyrics", + "@downloadAppleQqMultiPerson": { + "description": "Setting for word-by-word multi-person lyrics from Apple Music and QQ Music" + }, + "downloadAppleQqMultiPersonEnabled": "Speaker labels included for duets and group tracks", + "@downloadAppleQqMultiPersonEnabled": { + "description": "Subtitle when multi-person lyrics is on" + }, + "downloadAppleQqMultiPersonDisabled": "Standard lyrics without speaker labels", + "@downloadAppleQqMultiPersonDisabled": { + "description": "Subtitle when multi-person lyrics is off" + }, + "downloadMusixmatchLanguage": "Musixmatch Language", + "@downloadMusixmatchLanguage": { + "description": "Setting for Musixmatch lyrics translation language" + }, + "downloadMusixmatchLanguageAuto": "Auto (original language)", + "@downloadMusixmatchLanguageAuto": { + "description": "Subtitle when no language is set" + }, + "downloadFilterContributing": "Filter Contributing Artists", + "@downloadFilterContributing": { + "description": "Setting to strip contributing artists from Album Artist folder name" + }, + "downloadFilterContributingEnabled": "Contributing artists removed from Album Artist folder name", + "@downloadFilterContributingEnabled": { + "description": "Subtitle when contributing artist filter is on" + }, + "downloadFilterContributingDisabled": "Full Album Artist string used", + "@downloadFilterContributingDisabled": { + "description": "Subtitle when contributing artist filter is off" + }, + "downloadProvidersNoneEnabled": "No providers enabled", + "@downloadProvidersNoneEnabled": { + "description": "Shown when no lyrics providers are active" + }, + "downloadMusixmatchLanguageCode": "Language code", + "@downloadMusixmatchLanguageCode": { + "description": "Label for Musixmatch language input field" + }, + "downloadMusixmatchLanguageHint": "e.g. en, de, ja", + "@downloadMusixmatchLanguageHint": { + "description": "Placeholder for Musixmatch language input" + }, + "downloadMusixmatchLanguageDesc": "Enter a BCP-47 language code (e.g. en, de, ja) to request translated lyrics from Musixmatch.", + "@downloadMusixmatchLanguageDesc": { + "description": "Description in Musixmatch language picker" + }, + "downloadMusixmatchAuto": "Auto", + "@downloadMusixmatchAuto": { + "description": "Button to clear Musixmatch language (use auto)" + }, + "downloadNetworkAnySubtitle": "Use WiFi or mobile data", + "@downloadNetworkAnySubtitle": { + "description": "Subtitle for any-network option in picker" + }, + "downloadNetworkWifiOnlySubtitle": "Downloads pause when on mobile data", + "@downloadNetworkWifiOnlySubtitle": { + "description": "Subtitle for WiFi-only option in picker" + }, + "downloadSongLinkRegionDesc": "Region used when resolving track links via SongLink. Choose the country where your streaming services are available.", + "@downloadSongLinkRegionDesc": { + "description": "Description in SongLink region picker" + }, + "snackbarUnsupportedAudioFormat": "Unsupported audio format", + "@snackbarUnsupportedAudioFormat": { + "description": "Snackbar when the audio format is not supported for the requested operation" + }, + "cacheRefresh": "Refresh", + "@cacheRefresh": { + "description": "Tooltip for refresh button on cache management page" + }, + "dialogDownloadPlaylistsMessage": "Download {trackCount} {trackCount, plural, =1{track} other{tracks}} from {playlistCount} {playlistCount, plural, =1{playlist} other{playlists}}?", + "@dialogDownloadPlaylistsMessage": { + "description": "Dialog message for bulk playlist download confirmation", + "placeholders": { + "trackCount": { + "type": "int" + }, + "playlistCount": { + "type": "int" + } + } + }, + "bulkDownloadPlaylistsButton": "Download {count} {count, plural, =1{playlist} other{playlists}}", + "@bulkDownloadPlaylistsButton": { + "description": "Button label for bulk downloading selected playlists", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "bulkDownloadSelectPlaylists": "Select playlists to download", + "@bulkDownloadSelectPlaylists": { + "description": "Button label when no playlists are selected for download" + }, + "snackbarSelectedPlaylistsEmpty": "Selected playlists have no tracks", + "@snackbarSelectedPlaylistsEmpty": { + "description": "Snackbar when selected playlists contain no tracks" + }, + "playlistsCount": "{count, plural, =1{1 playlist} other{{count} playlists}}", + "@playlistsCount": { + "description": "Playlist count display", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "editMetadataAutoFill": "Auto-fill from online", + "@editMetadataAutoFill": { + "description": "Section title for selective online metadata auto-fill in the edit metadata sheet" + }, + "editMetadataAutoFillDesc": "Select fields to fill automatically from online metadata", + "@editMetadataAutoFillDesc": { + "description": "Description for the auto-fill section" + }, + "editMetadataAutoFillFetch": "Fetch & Fill", + "@editMetadataAutoFillFetch": { + "description": "Button label to fetch online metadata and fill selected fields" + }, + "editMetadataAutoFillSearching": "Searching online...", + "@editMetadataAutoFillSearching": { + "description": "Snackbar shown while searching for online metadata" + }, + "editMetadataAutoFillNoResults": "No matching metadata found online", + "@editMetadataAutoFillNoResults": { + "description": "Snackbar when online metadata search returns no results" + }, + "editMetadataAutoFillDone": "Filled {count} {count, plural, =1{field} other{fields}} from online metadata", + "@editMetadataAutoFillDone": { + "description": "Snackbar confirming how many fields were auto-filled", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "editMetadataAutoFillNoneSelected": "Select at least one field to auto-fill", + "@editMetadataAutoFillNoneSelected": { + "description": "Snackbar when user taps Fetch without selecting any fields" + }, + "editMetadataFieldTitle": "Title", + "@editMetadataFieldTitle": { + "description": "Chip label for title field in auto-fill selector" + }, + "editMetadataFieldArtist": "Artist", + "@editMetadataFieldArtist": { + "description": "Chip label for artist field in auto-fill selector" + }, + "editMetadataFieldAlbum": "Album", + "@editMetadataFieldAlbum": { + "description": "Chip label for album field in auto-fill selector" + }, + "editMetadataFieldAlbumArtist": "Album Artist", + "@editMetadataFieldAlbumArtist": { + "description": "Chip label for album artist field in auto-fill selector" + }, + "editMetadataFieldDate": "Date", + "@editMetadataFieldDate": { + "description": "Chip label for date field in auto-fill selector" + }, + "editMetadataFieldTrackNum": "Track #", + "@editMetadataFieldTrackNum": { + "description": "Chip label for track number field in auto-fill selector" + }, + "editMetadataFieldDiscNum": "Disc #", + "@editMetadataFieldDiscNum": { + "description": "Chip label for disc number field in auto-fill selector" + }, + "editMetadataFieldGenre": "Genre", + "@editMetadataFieldGenre": { + "description": "Chip label for genre field in auto-fill selector" + }, + "editMetadataFieldIsrc": "ISRC", + "@editMetadataFieldIsrc": { + "description": "Chip label for ISRC field in auto-fill selector" + }, + "editMetadataFieldLabel": "Label", + "@editMetadataFieldLabel": { + "description": "Chip label for label field in auto-fill selector" + }, + "editMetadataFieldCopyright": "Copyright", + "@editMetadataFieldCopyright": { + "description": "Chip label for copyright field in auto-fill selector" + }, + "editMetadataFieldCover": "Cover Art", + "@editMetadataFieldCover": { + "description": "Chip label for cover art field in auto-fill selector" + }, + "editMetadataSelectAll": "All", + "@editMetadataSelectAll": { + "description": "Button to select all fields for auto-fill" + }, + "editMetadataSelectEmpty": "Empty only", + "@editMetadataSelectEmpty": { + "description": "Button to select only fields that are currently empty" + }, + "queueDownloadingCount": "Downloading ({count})", + "@queueDownloadingCount": { + "description": "Header for active downloads section with count", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "queueDownloadedHeader": "Downloaded", + "@queueDownloadedHeader": { + "description": "Header label for downloaded items section in library" + }, + "queueFilteringIndicator": "Filtering...", + "@queueFilteringIndicator": { + "description": "Shown while filter results are being computed" + }, + "queueTrackCount": "{count, plural, =1{1 track} other{{count} tracks}}", + "@queueTrackCount": { + "description": "Track count label with plural support", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "queueAlbumCount": "{count, plural, =1{1 album} other{{count} albums}}", + "@queueAlbumCount": { + "description": "Album count label with plural support", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "queueEmptyAlbums": "No album downloads", + "@queueEmptyAlbums": { + "description": "Empty state title when no album downloads exist" + }, + "queueEmptyAlbumsSubtitle": "Download multiple tracks from an album to see them here", + "@queueEmptyAlbumsSubtitle": { + "description": "Empty state subtitle for album downloads" + }, + "queueEmptySingles": "No single downloads", + "@queueEmptySingles": { + "description": "Empty state title when no single track downloads exist" + }, + "queueEmptySinglesSubtitle": "Single track downloads will appear here", + "@queueEmptySinglesSubtitle": { + "description": "Empty state subtitle for single track downloads" + }, + "queueEmptyHistory": "No download history", + "@queueEmptyHistory": { + "description": "Empty state title when download history is empty" + }, + "queueEmptyHistorySubtitle": "Downloaded tracks will appear here", + "@queueEmptyHistorySubtitle": { + "description": "Empty state subtitle for download history" + }, + "selectionAllPlaylistsSelected": "All playlists selected", + "@selectionAllPlaylistsSelected": { + "description": "Shown when all playlists are selected in selection mode" + }, + "selectionTapPlaylistsToSelect": "Tap playlists to select", + "@selectionTapPlaylistsToSelect": { + "description": "Hint shown in playlist selection mode" + }, + "selectionSelectPlaylistsToDelete": "Select playlists to delete", + "@selectionSelectPlaylistsToDelete": { + "description": "Hint shown when no playlists are selected for deletion" + }, + "audioAnalysisTitle": "Audio Quality Analysis", + "@audioAnalysisTitle": { + "description": "Title for audio analysis section" + }, + "audioAnalysisDescription": "Verify lossless quality with spectrum analysis", + "@audioAnalysisDescription": { + "description": "Description for audio analysis tap-to-analyze prompt" + }, + "audioAnalysisAnalyzing": "Analyzing audio...", + "@audioAnalysisAnalyzing": { + "description": "Loading text while analyzing audio" + }, + "audioAnalysisSampleRate": "Sample Rate", + "@audioAnalysisSampleRate": { + "description": "Sample rate metric label" + }, + "audioAnalysisBitDepth": "Bit Depth", + "@audioAnalysisBitDepth": { + "description": "Bit depth metric label" + }, + "audioAnalysisChannels": "Channels", + "@audioAnalysisChannels": { + "description": "Channels metric label" + }, + "audioAnalysisDuration": "Duration", + "@audioAnalysisDuration": { + "description": "Duration metric label" + }, + "audioAnalysisNyquist": "Nyquist", + "@audioAnalysisNyquist": { + "description": "Nyquist frequency metric label" + }, + "audioAnalysisFileSize": "Size", + "@audioAnalysisFileSize": { + "description": "File size metric label" + }, + "audioAnalysisDynamicRange": "Dynamic Range", + "@audioAnalysisDynamicRange": { + "description": "Dynamic range metric label" + }, + "audioAnalysisPeak": "Peak", + "@audioAnalysisPeak": { + "description": "Peak amplitude metric label" + }, + "audioAnalysisRms": "RMS", + "@audioAnalysisRms": { + "description": "RMS level metric label" + }, + "audioAnalysisSamples": "Samples", + "@audioAnalysisSamples": { + "description": "Total samples metric label" + }, + "extensionsSearchWith": "Search with {providerName}", + "@extensionsSearchWith": { + "description": "Extensions page - subtitle for built-in search provider option", + "placeholders": { + "providerName": { + "type": "String" + } + } + }, + "extensionsHomeFeedProvider": "Home Feed Provider", + "@extensionsHomeFeedProvider": { + "description": "Extensions page - label for home feed provider selector" + }, + "extensionsHomeFeedDescription": "Choose which extension provides the home feed on the main screen", + "@extensionsHomeFeedDescription": { + "description": "Extensions page - description for home feed provider picker" + }, + "extensionsHomeFeedAuto": "Auto", + "@extensionsHomeFeedAuto": { + "description": "Label for auto-selected search provider" + }, + "extensionsHomeFeedAutoSubtitle": "Automatically select the best available", + "@extensionsHomeFeedAutoSubtitle": { + "description": "Extensions page - subtitle for auto home feed option" + }, + "extensionsHomeFeedUse": "Use {extensionName} home feed", + "@extensionsHomeFeedUse": { + "description": "Extensions page - subtitle for a specific extension home feed option", + "placeholders": { + "extensionName": { + "type": "String" + } + } + }, + "extensionsNoHomeFeedExtensions": "No extensions with home feed", + "@extensionsNoHomeFeedExtensions": { + "description": "Extensions page - shown when no installed extension has home feed" + }, + "sortAlphaAsc": "A-Z", + "@sortAlphaAsc": { + "description": "Sort option - alphabetical ascending" + }, + "sortAlphaDesc": "Z-A", + "@sortAlphaDesc": { + "description": "Sort option - alphabetical descending" + }, + "cancelDownloadTitle": "Cancel download?", + "@cancelDownloadTitle": { + "description": "Dialog title when confirming cancellation of an active download" + }, + "cancelDownloadContent": "This will cancel the active download for \"{trackName}\".", + "@cancelDownloadContent": { + "description": "Dialog body when confirming cancellation of an active download", + "placeholders": { + "trackName": { + "type": "String" + } + } + }, + "cancelDownloadKeep": "Keep", + "@cancelDownloadKeep": { + "description": "Dialog button - keep the active download (do not cancel)" + }, + "metadataSaveFailedFfmpeg": "Failed to save metadata via FFmpeg", + "@metadataSaveFailedFfmpeg": { + "description": "Snackbar error when FFmpeg fails to write metadata" + }, + "metadataSaveFailedStorage": "Failed to write metadata back to storage", + "@metadataSaveFailedStorage": { + "description": "Snackbar error when writing metadata file back to storage fails" + }, + "snackbarFolderPickerFailed": "Failed to open folder picker: {error}", + "@snackbarFolderPickerFailed": { + "description": "Snackbar shown when folder picker fails to open", + "placeholders": { + "error": { + "type": "String" + } + } + }, + "errorLoadAlbum": "Failed to load album", + "@errorLoadAlbum": { + "description": "Error state shown when album fails to load" + }, + "errorLoadPlaylist": "Failed to load playlist", + "@errorLoadPlaylist": { + "description": "Error state shown when playlist fails to load" + }, + "errorLoadArtist": "Failed to load artist", + "@errorLoadArtist": { + "description": "Error state shown when artist fails to load" + }, + "notifChannelDownloadName": "Download Progress", + "@notifChannelDownloadName": { + "description": "Android notification channel name for download progress" + }, + "notifChannelDownloadDesc": "Shows download progress for tracks", + "@notifChannelDownloadDesc": { + "description": "Android notification channel description for download progress" + }, + "notifChannelLibraryScanName": "Library Scan", + "@notifChannelLibraryScanName": { + "description": "Android notification channel name for library scan" + }, + "notifChannelLibraryScanDesc": "Shows local library scan progress", + "@notifChannelLibraryScanDesc": { + "description": "Android notification channel description for library scan" + }, + "notifDownloadingTrack": "Downloading {trackName}", + "@notifDownloadingTrack": { + "description": "Notification title while downloading a track", + "placeholders": { + "trackName": { + "type": "String" + } + } + }, + "notifFinalizingTrack": "Finalizing {trackName}", + "@notifFinalizingTrack": { + "description": "Notification title while finalizing (embedding metadata) a track", + "placeholders": { + "trackName": { + "type": "String" + } + } + }, + "notifEmbeddingMetadata": "Embedding metadata...", + "@notifEmbeddingMetadata": { + "description": "Notification body while embedding metadata into a downloaded track" + }, + "notifAlreadyInLibraryCount": "Already in Library ({completed}/{total})", + "@notifAlreadyInLibraryCount": { + "description": "Notification title when track is already in library, with count", + "placeholders": { + "completed": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "notifAlreadyInLibrary": "Already in Library", + "@notifAlreadyInLibrary": { + "description": "Notification title when track is already in library" + }, + "notifDownloadCompleteCount": "Download Complete ({completed}/{total})", + "@notifDownloadCompleteCount": { + "description": "Notification title when download is complete, with count", + "placeholders": { + "completed": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "notifDownloadComplete": "Download Complete", + "@notifDownloadComplete": { + "description": "Notification title when a single download is complete" + }, + "notifDownloadsFinished": "Downloads Finished ({completed} done, {failed} failed)", + "@notifDownloadsFinished": { + "description": "Notification title when queue finishes with some failures", + "placeholders": { + "completed": { + "type": "int" + }, + "failed": { + "type": "int" + } + } + }, + "notifAllDownloadsComplete": "All Downloads Complete", + "@notifAllDownloadsComplete": { + "description": "Notification title when all downloads finish successfully" + }, + "notifTracksDownloadedSuccess": "{count} tracks downloaded successfully", + "@notifTracksDownloadedSuccess": { + "description": "Notification body for queue complete - how many tracks were downloaded", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "notifScanningLibrary": "Scanning local library", + "@notifScanningLibrary": { + "description": "Notification title while scanning local library" + }, + "notifLibraryScanProgressWithTotal": "{scanned}/{total} files • {percentage}%", + "@notifLibraryScanProgressWithTotal": { + "description": "Notification body for library scan progress when total is known", + "placeholders": { + "scanned": { + "type": "int" + }, + "total": { + "type": "int" + }, + "percentage": { + "type": "int" + } + } + }, + "notifLibraryScanProgressNoTotal": "{scanned} files scanned • {percentage}%", + "@notifLibraryScanProgressNoTotal": { + "description": "Notification body for library scan progress when total is unknown", + "placeholders": { + "scanned": { + "type": "int" + }, + "percentage": { + "type": "int" + } + } + }, + "notifLibraryScanComplete": "Library scan complete", + "@notifLibraryScanComplete": { + "description": "Notification title when library scan finishes" + }, + "notifLibraryScanCompleteBody": "{count} tracks indexed", + "@notifLibraryScanCompleteBody": { + "description": "Notification body for library scan complete - number of indexed tracks", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "notifLibraryScanExcluded": "{count} excluded", + "@notifLibraryScanExcluded": { + "description": "Library scan complete suffix - excluded track count", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "notifLibraryScanErrors": "{count} errors", + "@notifLibraryScanErrors": { + "description": "Library scan complete suffix - error count", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "notifLibraryScanFailed": "Library scan failed", + "@notifLibraryScanFailed": { + "description": "Notification title when library scan fails" + }, + "notifLibraryScanCancelled": "Library scan cancelled", + "@notifLibraryScanCancelled": { + "description": "Notification title when library scan is cancelled by the user" + }, + "notifLibraryScanStopped": "Scan stopped before completion.", + "@notifLibraryScanStopped": { + "description": "Notification body when library scan is cancelled" + }, + "notifDownloadingUpdate": "Downloading SpotiFLAC v{version}", + "@notifDownloadingUpdate": { + "description": "Notification title while downloading an app update", + "placeholders": { + "version": { + "type": "String" + } + } + }, + "notifUpdateProgress": "{received} / {total} MB • {percentage}%", + "@notifUpdateProgress": { + "description": "Notification body showing update download progress", + "placeholders": { + "received": { + "type": "String" + }, + "total": { + "type": "String" + }, + "percentage": { + "type": "int" + } + } + }, + "notifUpdateReady": "Update Ready", + "@notifUpdateReady": { + "description": "Notification title when app update download is complete" + }, + "notifUpdateReadyBody": "SpotiFLAC v{version} downloaded. Tap to install.", + "@notifUpdateReadyBody": { + "description": "Notification body when app update is ready to install", + "placeholders": { + "version": { + "type": "String" + } + } + }, + "notifUpdateFailed": "Update Failed", + "@notifUpdateFailed": { + "description": "Notification title when app update download fails" + }, + "notifUpdateFailedBody": "Could not download update. Try again later.", + "@notifUpdateFailedBody": { + "description": "Notification body when app update download fails" + }, + "settingsFiles": "Files & Folders", + "@settingsFiles": { + "description": "Settings menu item - file and folder settings" + }, + "settingsFilesSubtitle": "Download location, filename, folder structure", + "@settingsFilesSubtitle": { + "description": "Subtitle for files & folders settings" + }, + "settingsMetadata": "Metadata", + "@settingsMetadata": { + "description": "Settings menu item - metadata settings" + }, + "settingsMetadataSubtitle": "Cover art, tags, ReplayGain, providers", + "@settingsMetadataSubtitle": { + "description": "Subtitle for metadata settings" + }, + "settingsLyrics": "Lyrics", + "@settingsLyrics": { + "description": "Settings menu item - lyrics settings" + }, + "settingsLyricsSubtitle": "Embed, mode, providers, language options", + "@settingsLyricsSubtitle": { + "description": "Subtitle for lyrics settings" + }, + "settingsApp": "App", + "@settingsApp": { + "description": "Settings menu item - app settings" + }, + "settingsAppSubtitle": "Updates, data, extension repo, debug", + "@settingsAppSubtitle": { + "description": "Subtitle for app settings" + }, + "sectionMetadataProviders": "Providers", + "@sectionMetadataProviders": { + "description": "Settings section header for metadata providers" + }, + "sectionDuplicates": "Duplicates", + "@sectionDuplicates": { + "description": "Settings section header for deduplication" + }, + "sectionLyricsProviderOptions": "Provider Options", + "@sectionLyricsProviderOptions": { + "description": "Settings section header for per-provider lyrics options" + }, + "metadataProvidersTitle": "Metadata Provider Priority", + "@metadataProvidersTitle": { + "description": "Settings item title for metadata provider order" + }, + "metadataProvidersSubtitle": "Drag to set search and metadata source order", + "@metadataProvidersSubtitle": { + "description": "Subtitle for metadata provider priority item" + }, + "downloadDeduplication": "Skip Duplicate Downloads", + "@downloadDeduplication": { + "description": "Setting - skip tracks already in download history" + }, + "downloadDeduplicationEnabled": "Already-downloaded tracks will be skipped", + "@downloadDeduplicationEnabled": { + "description": "Subtitle when deduplication is on" + }, + "downloadDeduplicationDisabled": "All tracks will be downloaded regardless of history", + "@downloadDeduplicationDisabled": { + "description": "Subtitle when deduplication is off" + }, + "downloadFallbackExtensions": "Fallback Extensions", + "@downloadFallbackExtensions": { + "description": "Settings item for configuring fallback extension providers" + }, + "downloadFallbackExtensionsSubtitle": "Choose which extensions can be used as fallback", + "@downloadFallbackExtensionsSubtitle": { + "description": "Subtitle for fallback extensions item" } } diff --git a/lib/l10n/arb/app_pt_PT.arb b/lib/l10n/arb/app_pt_PT.arb index 3cf494eb..5b7a11fc 100644 --- a/lib/l10n/arb/app_pt_PT.arb +++ b/lib/l10n/arb/app_pt_PT.arb @@ -4509,5 +4509,77 @@ "notifUpdateFailedBody": "Could not download update. Try again later.", "@notifUpdateFailedBody": { "description": "Notification body when app update download fails" + }, + "settingsFiles": "Files & Folders", + "@settingsFiles": { + "description": "Settings menu item - file and folder settings" + }, + "settingsFilesSubtitle": "Download location, filename, folder structure", + "@settingsFilesSubtitle": { + "description": "Subtitle for files & folders settings" + }, + "settingsMetadata": "Metadata", + "@settingsMetadata": { + "description": "Settings menu item - metadata settings" + }, + "settingsMetadataSubtitle": "Cover art, tags, ReplayGain, providers", + "@settingsMetadataSubtitle": { + "description": "Subtitle for metadata settings" + }, + "settingsLyrics": "Lyrics", + "@settingsLyrics": { + "description": "Settings menu item - lyrics settings" + }, + "settingsLyricsSubtitle": "Embed, mode, providers, language options", + "@settingsLyricsSubtitle": { + "description": "Subtitle for lyrics settings" + }, + "settingsApp": "App", + "@settingsApp": { + "description": "Settings menu item - app settings" + }, + "settingsAppSubtitle": "Updates, data, extension repo, debug", + "@settingsAppSubtitle": { + "description": "Subtitle for app settings" + }, + "sectionMetadataProviders": "Providers", + "@sectionMetadataProviders": { + "description": "Settings section header for metadata providers" + }, + "sectionDuplicates": "Duplicates", + "@sectionDuplicates": { + "description": "Settings section header for deduplication" + }, + "sectionLyricsProviderOptions": "Provider Options", + "@sectionLyricsProviderOptions": { + "description": "Settings section header for per-provider lyrics options" + }, + "metadataProvidersTitle": "Metadata Provider Priority", + "@metadataProvidersTitle": { + "description": "Settings item title for metadata provider order" + }, + "metadataProvidersSubtitle": "Drag to set search and metadata source order", + "@metadataProvidersSubtitle": { + "description": "Subtitle for metadata provider priority item" + }, + "downloadDeduplication": "Skip Duplicate Downloads", + "@downloadDeduplication": { + "description": "Setting - skip tracks already in download history" + }, + "downloadDeduplicationEnabled": "Already-downloaded tracks will be skipped", + "@downloadDeduplicationEnabled": { + "description": "Subtitle when deduplication is on" + }, + "downloadDeduplicationDisabled": "All tracks will be downloaded regardless of history", + "@downloadDeduplicationDisabled": { + "description": "Subtitle when deduplication is off" + }, + "downloadFallbackExtensions": "Fallback Extensions", + "@downloadFallbackExtensions": { + "description": "Settings item for configuring fallback extension providers" + }, + "downloadFallbackExtensionsSubtitle": "Choose which extensions can be used as fallback", + "@downloadFallbackExtensionsSubtitle": { + "description": "Subtitle for fallback extensions item" } } diff --git a/lib/l10n/arb/app_ru.arb b/lib/l10n/arb/app_ru.arb index c3d650fa..54b47b1a 100644 --- a/lib/l10n/arb/app_ru.arb +++ b/lib/l10n/arb/app_ru.arb @@ -4509,5 +4509,77 @@ "notifUpdateFailedBody": "Could not download update. Try again later.", "@notifUpdateFailedBody": { "description": "Notification body when app update download fails" + }, + "settingsFiles": "Files & Folders", + "@settingsFiles": { + "description": "Settings menu item - file and folder settings" + }, + "settingsFilesSubtitle": "Download location, filename, folder structure", + "@settingsFilesSubtitle": { + "description": "Subtitle for files & folders settings" + }, + "settingsMetadata": "Metadata", + "@settingsMetadata": { + "description": "Settings menu item - metadata settings" + }, + "settingsMetadataSubtitle": "Cover art, tags, ReplayGain, providers", + "@settingsMetadataSubtitle": { + "description": "Subtitle for metadata settings" + }, + "settingsLyrics": "Lyrics", + "@settingsLyrics": { + "description": "Settings menu item - lyrics settings" + }, + "settingsLyricsSubtitle": "Embed, mode, providers, language options", + "@settingsLyricsSubtitle": { + "description": "Subtitle for lyrics settings" + }, + "settingsApp": "App", + "@settingsApp": { + "description": "Settings menu item - app settings" + }, + "settingsAppSubtitle": "Updates, data, extension repo, debug", + "@settingsAppSubtitle": { + "description": "Subtitle for app settings" + }, + "sectionMetadataProviders": "Providers", + "@sectionMetadataProviders": { + "description": "Settings section header for metadata providers" + }, + "sectionDuplicates": "Duplicates", + "@sectionDuplicates": { + "description": "Settings section header for deduplication" + }, + "sectionLyricsProviderOptions": "Provider Options", + "@sectionLyricsProviderOptions": { + "description": "Settings section header for per-provider lyrics options" + }, + "metadataProvidersTitle": "Metadata Provider Priority", + "@metadataProvidersTitle": { + "description": "Settings item title for metadata provider order" + }, + "metadataProvidersSubtitle": "Drag to set search and metadata source order", + "@metadataProvidersSubtitle": { + "description": "Subtitle for metadata provider priority item" + }, + "downloadDeduplication": "Skip Duplicate Downloads", + "@downloadDeduplication": { + "description": "Setting - skip tracks already in download history" + }, + "downloadDeduplicationEnabled": "Already-downloaded tracks will be skipped", + "@downloadDeduplicationEnabled": { + "description": "Subtitle when deduplication is on" + }, + "downloadDeduplicationDisabled": "All tracks will be downloaded regardless of history", + "@downloadDeduplicationDisabled": { + "description": "Subtitle when deduplication is off" + }, + "downloadFallbackExtensions": "Fallback Extensions", + "@downloadFallbackExtensions": { + "description": "Settings item for configuring fallback extension providers" + }, + "downloadFallbackExtensionsSubtitle": "Choose which extensions can be used as fallback", + "@downloadFallbackExtensionsSubtitle": { + "description": "Subtitle for fallback extensions item" } } \ No newline at end of file diff --git a/lib/l10n/arb/app_tr.arb b/lib/l10n/arb/app_tr.arb index e0aa8df7..9ffbe3b0 100644 --- a/lib/l10n/arb/app_tr.arb +++ b/lib/l10n/arb/app_tr.arb @@ -4509,5 +4509,77 @@ "notifUpdateFailedBody": "Could not download update. Try again later.", "@notifUpdateFailedBody": { "description": "Notification body when app update download fails" + }, + "settingsFiles": "Files & Folders", + "@settingsFiles": { + "description": "Settings menu item - file and folder settings" + }, + "settingsFilesSubtitle": "Download location, filename, folder structure", + "@settingsFilesSubtitle": { + "description": "Subtitle for files & folders settings" + }, + "settingsMetadata": "Metadata", + "@settingsMetadata": { + "description": "Settings menu item - metadata settings" + }, + "settingsMetadataSubtitle": "Cover art, tags, ReplayGain, providers", + "@settingsMetadataSubtitle": { + "description": "Subtitle for metadata settings" + }, + "settingsLyrics": "Lyrics", + "@settingsLyrics": { + "description": "Settings menu item - lyrics settings" + }, + "settingsLyricsSubtitle": "Embed, mode, providers, language options", + "@settingsLyricsSubtitle": { + "description": "Subtitle for lyrics settings" + }, + "settingsApp": "App", + "@settingsApp": { + "description": "Settings menu item - app settings" + }, + "settingsAppSubtitle": "Updates, data, extension repo, debug", + "@settingsAppSubtitle": { + "description": "Subtitle for app settings" + }, + "sectionMetadataProviders": "Providers", + "@sectionMetadataProviders": { + "description": "Settings section header for metadata providers" + }, + "sectionDuplicates": "Duplicates", + "@sectionDuplicates": { + "description": "Settings section header for deduplication" + }, + "sectionLyricsProviderOptions": "Provider Options", + "@sectionLyricsProviderOptions": { + "description": "Settings section header for per-provider lyrics options" + }, + "metadataProvidersTitle": "Metadata Provider Priority", + "@metadataProvidersTitle": { + "description": "Settings item title for metadata provider order" + }, + "metadataProvidersSubtitle": "Drag to set search and metadata source order", + "@metadataProvidersSubtitle": { + "description": "Subtitle for metadata provider priority item" + }, + "downloadDeduplication": "Skip Duplicate Downloads", + "@downloadDeduplication": { + "description": "Setting - skip tracks already in download history" + }, + "downloadDeduplicationEnabled": "Already-downloaded tracks will be skipped", + "@downloadDeduplicationEnabled": { + "description": "Subtitle when deduplication is on" + }, + "downloadDeduplicationDisabled": "All tracks will be downloaded regardless of history", + "@downloadDeduplicationDisabled": { + "description": "Subtitle when deduplication is off" + }, + "downloadFallbackExtensions": "Fallback Extensions", + "@downloadFallbackExtensions": { + "description": "Settings item for configuring fallback extension providers" + }, + "downloadFallbackExtensionsSubtitle": "Choose which extensions can be used as fallback", + "@downloadFallbackExtensionsSubtitle": { + "description": "Subtitle for fallback extensions item" } } \ No newline at end of file diff --git a/lib/l10n/arb/app_uk.arb b/lib/l10n/arb/app_uk.arb index 62c47453..d9f2ed50 100644 --- a/lib/l10n/arb/app_uk.arb +++ b/lib/l10n/arb/app_uk.arb @@ -4509,5 +4509,77 @@ "notifUpdateFailedBody": "Не вдалося завантажити оновлення. Спробуйте пізніше.", "@notifUpdateFailedBody": { "description": "Notification body when app update download fails" + }, + "settingsFiles": "Files & Folders", + "@settingsFiles": { + "description": "Settings menu item - file and folder settings" + }, + "settingsFilesSubtitle": "Download location, filename, folder structure", + "@settingsFilesSubtitle": { + "description": "Subtitle for files & folders settings" + }, + "settingsMetadata": "Metadata", + "@settingsMetadata": { + "description": "Settings menu item - metadata settings" + }, + "settingsMetadataSubtitle": "Cover art, tags, ReplayGain, providers", + "@settingsMetadataSubtitle": { + "description": "Subtitle for metadata settings" + }, + "settingsLyrics": "Lyrics", + "@settingsLyrics": { + "description": "Settings menu item - lyrics settings" + }, + "settingsLyricsSubtitle": "Embed, mode, providers, language options", + "@settingsLyricsSubtitle": { + "description": "Subtitle for lyrics settings" + }, + "settingsApp": "App", + "@settingsApp": { + "description": "Settings menu item - app settings" + }, + "settingsAppSubtitle": "Updates, data, extension repo, debug", + "@settingsAppSubtitle": { + "description": "Subtitle for app settings" + }, + "sectionMetadataProviders": "Providers", + "@sectionMetadataProviders": { + "description": "Settings section header for metadata providers" + }, + "sectionDuplicates": "Duplicates", + "@sectionDuplicates": { + "description": "Settings section header for deduplication" + }, + "sectionLyricsProviderOptions": "Provider Options", + "@sectionLyricsProviderOptions": { + "description": "Settings section header for per-provider lyrics options" + }, + "metadataProvidersTitle": "Metadata Provider Priority", + "@metadataProvidersTitle": { + "description": "Settings item title for metadata provider order" + }, + "metadataProvidersSubtitle": "Drag to set search and metadata source order", + "@metadataProvidersSubtitle": { + "description": "Subtitle for metadata provider priority item" + }, + "downloadDeduplication": "Skip Duplicate Downloads", + "@downloadDeduplication": { + "description": "Setting - skip tracks already in download history" + }, + "downloadDeduplicationEnabled": "Already-downloaded tracks will be skipped", + "@downloadDeduplicationEnabled": { + "description": "Subtitle when deduplication is on" + }, + "downloadDeduplicationDisabled": "All tracks will be downloaded regardless of history", + "@downloadDeduplicationDisabled": { + "description": "Subtitle when deduplication is off" + }, + "downloadFallbackExtensions": "Fallback Extensions", + "@downloadFallbackExtensions": { + "description": "Settings item for configuring fallback extension providers" + }, + "downloadFallbackExtensionsSubtitle": "Choose which extensions can be used as fallback", + "@downloadFallbackExtensionsSubtitle": { + "description": "Subtitle for fallback extensions item" } } \ No newline at end of file diff --git a/lib/l10n/arb/app_zh.arb b/lib/l10n/arb/app_zh.arb index 307f156b..3127c84e 100644 --- a/lib/l10n/arb/app_zh.arb +++ b/lib/l10n/arb/app_zh.arb @@ -1728,5 +1728,2858 @@ "type": "int" } } + }, + "navLibrary": "Library", + "@navLibrary": { + "description": "Bottom navigation - Library tab" + }, + "historySearchHint": "Search history...", + "@historySearchHint": { + "description": "Search bar placeholder in history" + }, + "downloadSingleFilenameFormat": "Single Filename Format", + "@downloadSingleFilenameFormat": { + "description": "Setting for output filename pattern for singles/EPs" + }, + "downloadSingleFilenameFormatDescription": "Filename pattern for singles and EPs. Uses the same tags as the album format.", + "@downloadSingleFilenameFormatDescription": { + "description": "Subtitle description for single filename format setting" + }, + "optionsDefaultSearchTab": "Default Search Tab", + "@optionsDefaultSearchTab": { + "description": "Title for the preferred default search tab setting" + }, + "optionsDefaultSearchTabSubtitle": "Choose which tab opens first for new search results.", + "@optionsDefaultSearchTabSubtitle": { + "description": "Subtitle for the preferred default search tab setting" + }, + "optionsReplayGain": "ReplayGain", + "@optionsReplayGain": { + "description": "Title for ReplayGain setting toggle" + }, + "optionsReplayGainSubtitleOn": "Scan loudness and embed ReplayGain tags (EBU R128)", + "@optionsReplayGainSubtitleOn": { + "description": "Subtitle when ReplayGain is enabled" + }, + "optionsReplayGainSubtitleOff": "Disabled: no loudness normalization tags", + "@optionsReplayGainSubtitleOff": { + "description": "Subtitle when ReplayGain is disabled" + }, + "optionsArtistTagMode": "Artist Tag Mode", + "@optionsArtistTagMode": { + "description": "Setting title for how artist metadata is written into files" + }, + "optionsArtistTagModeDescription": "Choose how multiple artists are written into embedded tags.", + "@optionsArtistTagModeDescription": { + "description": "Bottom-sheet description for artist tag mode setting" + }, + "optionsArtistTagModeJoined": "Single joined value", + "@optionsArtistTagModeJoined": { + "description": "Artist tag mode option that joins multiple artists into one value" + }, + "optionsArtistTagModeJoinedSubtitle": "Write one ARTIST value like \"Artist A, Artist B\" for maximum player compatibility.", + "@optionsArtistTagModeJoinedSubtitle": { + "description": "Subtitle for joined artist tag mode" + }, + "optionsArtistTagModeSplitVorbis": "Split tags for FLAC/Opus", + "@optionsArtistTagModeSplitVorbis": { + "description": "Artist tag mode option that writes repeated ARTIST tags for Vorbis formats" + }, + "optionsArtistTagModeSplitVorbisSubtitle": "Write one artist tag per artist for FLAC and Opus; MP3 and M4A stay joined.", + "@optionsArtistTagModeSplitVorbisSubtitle": { + "description": "Subtitle for split Vorbis artist tag mode" + }, + "optionsSpotifyDeprecationWarning": "Spotify search will be deprecated on March 3, 2026 due to Spotify API changes. Please switch to Deezer.", + "@optionsSpotifyDeprecationWarning": { + "description": "Warning about Spotify API deprecation" + }, + "aboutTranslators": "Translators", + "@aboutTranslators": { + "description": "Section for translators" + }, + "aboutTelegramChannel": "Telegram Channel", + "@aboutTelegramChannel": { + "description": "Link to Telegram channel" + }, + "aboutTelegramChannelSubtitle": "Announcements and updates", + "@aboutTelegramChannelSubtitle": { + "description": "Subtitle for Telegram channel" + }, + "aboutTelegramChat": "Telegram Community", + "@aboutTelegramChat": { + "description": "Link to Telegram chat group" + }, + "aboutTelegramChatSubtitle": "Chat with other users", + "@aboutTelegramChatSubtitle": { + "description": "Subtitle for Telegram chat" + }, + "aboutSocial": "Social", + "@aboutSocial": { + "description": "Section for social links" + }, + "aboutSjdonadoDesc": "Creator of I Don't Have Spotify (IDHS). The fallback link resolver that saves the day!", + "@aboutSjdonadoDesc": { + "description": "Credit description for sjdonado" + }, + "aboutSpotiSaver": "SpotiSaver", + "@aboutSpotiSaver": { + "description": "Name of SpotiSaver API service - DO NOT TRANSLATE" + }, + "aboutSpotiSaverDesc": "Tidal Hi-Res FLAC streaming endpoints. A key piece of the lossless puzzle!", + "@aboutSpotiSaverDesc": { + "description": "Credit for SpotiSaver API" + }, + "artistPopular": "Popular", + "@artistPopular": { + "description": "Section header for popular/top tracks" + }, + "artistMonthlyListeners": "{count} monthly listeners", + "@artistMonthlyListeners": { + "description": "Monthly listener count display", + "placeholders": { + "count": { + "type": "String", + "description": "Formatted listener count" + } + } + }, + "setupIcloudNotSupported": "iCloud Drive is not supported. Please use the app Documents folder.", + "@setupIcloudNotSupported": { + "description": "Error when user selects iCloud Drive on iOS" + }, + "dialogDownload": "Download", + "@dialogDownload": { + "description": "Confirm button in Download All dialog" + }, + "csvImportTracks": "{count} tracks from CSV", + "@csvImportTracks": { + "description": "Label shown in quality picker for CSV import", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "snackbarAlreadyInLibrary": "\"{trackName}\" already exists in your library", + "@snackbarAlreadyInLibrary": { + "description": "Snackbar - track already exists in local library", + "placeholders": { + "trackName": { + "type": "String" + } + } + }, + "errorUrlNotRecognized": "Link not recognized", + "@errorUrlNotRecognized": { + "description": "Error title - URL not handled by any extension or service" + }, + "errorUrlNotRecognizedMessage": "This link is not supported. Make sure the URL is correct and a compatible extension is installed.", + "@errorUrlNotRecognizedMessage": { + "description": "Error message - URL not recognized explanation" + }, + "errorUrlFetchFailed": "Failed to load content from this link. Please try again.", + "@errorUrlFetchFailed": { + "description": "Error message - generic URL fetch failure" + }, + "searchSortTitle": "Sort Results", + "@searchSortTitle": { + "description": "Bottom sheet title for search sort options" + }, + "searchSortDefault": "Default", + "@searchSortDefault": { + "description": "Sort option - default API order" + }, + "searchSortTitleAZ": "Title (A-Z)", + "@searchSortTitleAZ": { + "description": "Sort option - title ascending" + }, + "searchSortTitleZA": "Title (Z-A)", + "@searchSortTitleZA": { + "description": "Sort option - title descending" + }, + "searchSortArtistAZ": "Artist (A-Z)", + "@searchSortArtistAZ": { + "description": "Sort option - artist ascending" + }, + "searchSortArtistZA": "Artist (Z-A)", + "@searchSortArtistZA": { + "description": "Sort option - artist descending" + }, + "searchSortDurationShort": "Duration (Shortest)", + "@searchSortDurationShort": { + "description": "Sort option - shortest duration first" + }, + "searchSortDurationLong": "Duration (Longest)", + "@searchSortDurationLong": { + "description": "Sort option - longest duration first" + }, + "searchSortDateOldest": "Release Date (Oldest)", + "@searchSortDateOldest": { + "description": "Sort option - oldest release first" + }, + "searchSortDateNewest": "Release Date (Newest)", + "@searchSortDateNewest": { + "description": "Sort option - newest release first" + }, + "filenameShowAdvancedTags": "Show advanced tags", + "@filenameShowAdvancedTags": { + "description": "Toggle label for showing advanced filename tags" + }, + "filenameShowAdvancedTagsDescription": "Enable formatted tags for track padding and date patterns", + "@filenameShowAdvancedTagsDescription": { + "description": "Description for advanced filename tag toggle" + }, + "folderOrganizationByPlaylist": "By Playlist", + "@folderOrganizationByPlaylist": { + "description": "Folder option - playlist folders" + }, + "folderOrganizationByPlaylistSubtitle": "Separate folder for each playlist", + "@folderOrganizationByPlaylistSubtitle": { + "description": "Subtitle for playlist folder option" + }, + "providerPriorityFallbackExtensionsTitle": "Extension Fallback", + "@providerPriorityFallbackExtensionsTitle": { + "description": "Section title for choosing which download extensions can be used as fallback providers" + }, + "providerPriorityFallbackExtensionsDescription": "Choose which installed download extensions can be used during automatic fallback. Built-in providers still follow the priority order above.", + "@providerPriorityFallbackExtensionsDescription": { + "description": "Section description for extension fallback selection" + }, + "providerPriorityFallbackExtensionsHint": "Only enabled extensions with download-provider capability are listed here.", + "@providerPriorityFallbackExtensionsHint": { + "description": "Hint below the extension fallback selection list" + }, + "sectionLyrics": "Lyrics", + "@sectionLyrics": { + "description": "Settings section header" + }, + "lyricsMode": "Lyrics Mode", + "@lyricsMode": { + "description": "Setting - how to save lyrics" + }, + "lyricsModeDescription": "Choose how lyrics are saved with your downloads", + "@lyricsModeDescription": { + "description": "Lyrics mode picker description" + }, + "lyricsModeEmbed": "Embed in file", + "@lyricsModeEmbed": { + "description": "Lyrics mode option - embed in audio file" + }, + "lyricsModeEmbedSubtitle": "Lyrics stored inside FLAC metadata", + "@lyricsModeEmbedSubtitle": { + "description": "Subtitle for embed option" + }, + "lyricsModeExternal": "External .lrc file", + "@lyricsModeExternal": { + "description": "Lyrics mode option - separate LRC file" + }, + "lyricsModeExternalSubtitle": "Separate .lrc file for players like Samsung Music", + "@lyricsModeExternalSubtitle": { + "description": "Subtitle for external option" + }, + "lyricsModeBoth": "Both", + "@lyricsModeBoth": { + "description": "Lyrics mode option - embed and external" + }, + "lyricsModeBothSubtitle": "Embed and save .lrc file", + "@lyricsModeBothSubtitle": { + "description": "Subtitle for both option" + }, + "trackGenre": "Genre", + "@trackGenre": { + "description": "Metadata label - music genre" + }, + "trackLabel": "Label", + "@trackLabel": { + "description": "Metadata label - record label" + }, + "trackCopyright": "Copyright", + "@trackCopyright": { + "description": "Metadata label - copyright information" + }, + "trackLyricsNotInFile": "No lyrics found in this file", + "@trackLyricsNotInFile": { + "description": "Message when no embedded lyrics in audio file" + }, + "trackFetchOnlineLyrics": "Fetch from Online", + "@trackFetchOnlineLyrics": { + "description": "Action - fetch lyrics from online providers" + }, + "trackEmbedLyrics": "Embed Lyrics", + "@trackEmbedLyrics": { + "description": "Action - embed lyrics into audio file" + }, + "trackLyricsEmbedded": "Lyrics embedded successfully", + "@trackLyricsEmbedded": { + "description": "Snackbar - lyrics saved to file" + }, + "trackInstrumental": "Instrumental track", + "@trackInstrumental": { + "description": "Message when track is instrumental (no lyrics)" + }, + "storeAddRepoTitle": "Add Extension Repository", + "@storeAddRepoTitle": { + "description": "Store setup screen - heading when no repo is configured" + }, + "storeAddRepoDescription": "Enter a GitHub repository URL that contains a registry.json file to browse and install extensions.", + "@storeAddRepoDescription": { + "description": "Store setup screen - explanatory text" + }, + "storeRepoUrlLabel": "Repository URL", + "@storeRepoUrlLabel": { + "description": "Label for the repository URL input field" + }, + "storeRepoUrlHint": "https://github.com/user/repo", + "@storeRepoUrlHint": { + "description": "Hint/placeholder for the repository URL input field" + }, + "storeRepoUrlHelper": "e.g. https://github.com/user/extensions-repo", + "@storeRepoUrlHelper": { + "description": "Helper text below the repository URL input field" + }, + "storeAddRepoButton": "Add Repository", + "@storeAddRepoButton": { + "description": "Button to submit a new repository URL" + }, + "storeChangeRepoTooltip": "Change repository", + "@storeChangeRepoTooltip": { + "description": "Tooltip for the change-repository icon button in the app bar" + }, + "storeRepoDialogTitle": "Extension Repository", + "@storeRepoDialogTitle": { + "description": "Title of the change/remove repository dialog" + }, + "storeRepoDialogCurrent": "Current repository:", + "@storeRepoDialogCurrent": { + "description": "Label shown above the current repository URL in the dialog" + }, + "storeNewRepoUrlLabel": "New Repository URL", + "@storeNewRepoUrlLabel": { + "description": "Label for the new repository URL field inside the dialog" + }, + "storeLoadError": "Failed to load repository", + "@storeLoadError": { + "description": "Error heading when the store cannot be loaded" + }, + "storeEmptyNoExtensions": "No extensions available", + "@storeEmptyNoExtensions": { + "description": "Message when store has no extensions" + }, + "storeEmptyNoResults": "No extensions found", + "@storeEmptyNoResults": { + "description": "Message when search/filter returns no results" + }, + "extensionsFallbackTitle": "Fallback Extensions", + "@extensionsFallbackTitle": { + "description": "Setting and page title for choosing which download extensions can be used during fallback" + }, + "extensionsFallbackSubtitle": "Choose which installed download extensions can be used as fallback", + "@extensionsFallbackSubtitle": { + "description": "Subtitle for download fallback extensions menu" + }, + "downloadLossy320": "Lossy 320kbps", + "@downloadLossy320": { + "description": "Quality option label for Tidal lossy 320kbps" + }, + "downloadLossyFormat": "Lossy Format", + "@downloadLossyFormat": { + "description": "Setting title to pick output format for Tidal lossy downloads" + }, + "downloadLossy320Format": "Lossy 320kbps Format", + "@downloadLossy320Format": { + "description": "Title of the Tidal lossy format picker bottom sheet" + }, + "downloadLossy320FormatDesc": "Choose the output format for Tidal 320kbps lossy downloads. The original AAC stream will be converted to your selected format.", + "@downloadLossy320FormatDesc": { + "description": "Description in the Tidal lossy format picker" + }, + "downloadLossyMp3": "MP3 320kbps", + "@downloadLossyMp3": { + "description": "Tidal lossy format option - MP3 320kbps" + }, + "downloadLossyMp3Subtitle": "Best compatibility, ~10MB per track", + "@downloadLossyMp3Subtitle": { + "description": "Subtitle for MP3 320kbps Tidal lossy option" + }, + "downloadLossyOpus256": "Opus 256kbps", + "@downloadLossyOpus256": { + "description": "Tidal lossy format option - Opus 256kbps" + }, + "downloadLossyOpus256Subtitle": "Best quality Opus, ~8MB per track", + "@downloadLossyOpus256Subtitle": { + "description": "Subtitle for Opus 256kbps Tidal lossy option" + }, + "downloadLossyOpus128": "Opus 128kbps", + "@downloadLossyOpus128": { + "description": "Tidal lossy format option - Opus 128kbps" + }, + "downloadLossyOpus128Subtitle": "Smallest size, ~4MB per track", + "@downloadLossyOpus128Subtitle": { + "description": "Subtitle for Opus 128kbps Tidal lossy option" + }, + "downloadUseAlbumArtistForFolders": "Use Album Artist for folders", + "@downloadUseAlbumArtistForFolders": { + "description": "Setting - choose whether artist folders use Album Artist or Track Artist" + }, + "downloadUsePrimaryArtistOnly": "Primary artist only for folders", + "@downloadUsePrimaryArtistOnly": { + "description": "Setting - strip featured artists from folder name" + }, + "downloadUsePrimaryArtistOnlyEnabled": "Featured artists removed from folder name (e.g. Justin Bieber, Quavo → Justin Bieber)", + "@downloadUsePrimaryArtistOnlyEnabled": { + "description": "Subtitle when primary artist only is enabled" + }, + "downloadUsePrimaryArtistOnlyDisabled": "Full artist string used for folder name", + "@downloadUsePrimaryArtistOnlyDisabled": { + "description": "Subtitle when primary artist only is disabled" + }, + "settingsAutoExportFailed": "Auto-export failed downloads", + "@settingsAutoExportFailed": { + "description": "Setting toggle for auto-export" + }, + "settingsAutoExportFailedSubtitle": "Save failed downloads to TXT file automatically", + "@settingsAutoExportFailedSubtitle": { + "description": "Subtitle for auto-export setting" + }, + "settingsDownloadNetwork": "Download Network", + "@settingsDownloadNetwork": { + "description": "Setting for network type preference" + }, + "settingsDownloadNetworkAny": "WiFi + Mobile Data", + "@settingsDownloadNetworkAny": { + "description": "Network option - use any connection" + }, + "settingsDownloadNetworkWifiOnly": "WiFi Only", + "@settingsDownloadNetworkWifiOnly": { + "description": "Network option - only use WiFi" + }, + "settingsDownloadNetworkSubtitle": "Choose which network to use for downloads. When set to WiFi Only, downloads will pause on mobile data.", + "@settingsDownloadNetworkSubtitle": { + "description": "Subtitle explaining network preference" + }, + "albumFolderArtistAlbumSingles": "Artist / Album + Singles", + "@albumFolderArtistAlbumSingles": { + "description": "Album folder option with singles inside artist" + }, + "albumFolderArtistAlbumSinglesSubtitle": "Artist/Album/ and Artist/Singles/", + "@albumFolderArtistAlbumSinglesSubtitle": { + "description": "Folder structure example" + }, + "albumFolderArtistAlbumFlat": "Artist / Album (Singles flat)", + "@albumFolderArtistAlbumFlat": { + "description": "Album folder option with singles directly in artist folder" + }, + "albumFolderArtistAlbumFlatSubtitle": "Artist/Album/ and Artist/song.flac", + "@albumFolderArtistAlbumFlatSubtitle": { + "description": "Folder structure example for flat singles" + }, + "downloadedAlbumDiscHeader": "Disc {discNumber}", + "@downloadedAlbumDiscHeader": { + "description": "Header for disc separator in multi-disc albums", + "placeholders": { + "discNumber": { + "type": "int", + "example": "1" + } + } + }, + "recentTypeArtist": "Artist", + "@recentTypeArtist": { + "description": "Recent access item type - artist" + }, + "recentTypeAlbum": "Album", + "@recentTypeAlbum": { + "description": "Recent access item type - album" + }, + "recentTypeSong": "Song", + "@recentTypeSong": { + "description": "Recent access item type - song/track" + }, + "recentTypePlaylist": "Playlist", + "@recentTypePlaylist": { + "description": "Recent access item type - playlist" + }, + "recentEmpty": "No recent items yet", + "@recentEmpty": { + "description": "Empty state text for recent access list" + }, + "recentShowAllDownloads": "Show All Downloads", + "@recentShowAllDownloads": { + "description": "Button label to unhide hidden downloads in recent access" + }, + "recentPlaylistInfo": "Playlist: {name}", + "@recentPlaylistInfo": { + "description": "Snackbar message when tapping playlist in recent access", + "placeholders": { + "name": { + "type": "String", + "description": "Playlist name" + } + } + }, + "discographyDownload": "Download Discography", + "@discographyDownload": { + "description": "Button - download artist discography" + }, + "discographyDownloadAll": "Download All", + "@discographyDownloadAll": { + "description": "Option - download entire discography" + }, + "discographyDownloadAllSubtitle": "{count} tracks from {albumCount} releases", + "@discographyDownloadAllSubtitle": { + "description": "Subtitle showing total tracks and albums", + "placeholders": { + "count": { + "type": "int" + }, + "albumCount": { + "type": "int" + } + } + }, + "discographyAlbumsOnly": "Albums Only", + "@discographyAlbumsOnly": { + "description": "Option - download only albums" + }, + "discographyAlbumsOnlySubtitle": "{count} tracks from {albumCount} albums", + "@discographyAlbumsOnlySubtitle": { + "description": "Subtitle showing album tracks count", + "placeholders": { + "count": { + "type": "int" + }, + "albumCount": { + "type": "int" + } + } + }, + "discographySinglesOnly": "Singles & EPs Only", + "@discographySinglesOnly": { + "description": "Option - download only singles" + }, + "discographySinglesOnlySubtitle": "{count} tracks from {albumCount} singles", + "@discographySinglesOnlySubtitle": { + "description": "Subtitle showing singles tracks count", + "placeholders": { + "count": { + "type": "int" + }, + "albumCount": { + "type": "int" + } + } + }, + "discographySelectAlbums": "Select Albums...", + "@discographySelectAlbums": { + "description": "Option - manually select albums to download" + }, + "discographySelectAlbumsSubtitle": "Choose specific albums or singles", + "@discographySelectAlbumsSubtitle": { + "description": "Subtitle for select albums option" + }, + "discographyFetchingTracks": "Fetching tracks...", + "@discographyFetchingTracks": { + "description": "Progress - fetching album tracks" + }, + "discographyFetchingAlbum": "Fetching {current} of {total}...", + "@discographyFetchingAlbum": { + "description": "Progress - fetching specific album", + "placeholders": { + "current": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "discographySelectedCount": "{count} selected", + "@discographySelectedCount": { + "description": "Selection count badge", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "discographyDownloadSelected": "Download Selected", + "@discographyDownloadSelected": { + "description": "Button - download selected albums" + }, + "discographyAddedToQueue": "Added {count} tracks to queue", + "@discographyAddedToQueue": { + "description": "Snackbar - tracks added from discography", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "discographySkippedDownloaded": "{added} added, {skipped} already downloaded", + "@discographySkippedDownloaded": { + "description": "Snackbar - with skipped tracks count", + "placeholders": { + "added": { + "type": "int" + }, + "skipped": { + "type": "int" + } + } + }, + "discographyNoAlbums": "No albums available", + "@discographyNoAlbums": { + "description": "Error - no albums found for artist" + }, + "discographyFailedToFetch": "Failed to fetch some albums", + "@discographyFailedToFetch": { + "description": "Error - some albums failed to load" + }, + "sectionStorageAccess": "Storage Access", + "@sectionStorageAccess": { + "description": "Section header for storage access settings" + }, + "allFilesAccess": "All Files Access", + "@allFilesAccess": { + "description": "Toggle for MANAGE_EXTERNAL_STORAGE permission" + }, + "allFilesAccessEnabledSubtitle": "Can write to any folder", + "@allFilesAccessEnabledSubtitle": { + "description": "Subtitle when all files access is enabled" + }, + "allFilesAccessDisabledSubtitle": "Limited to media folders only", + "@allFilesAccessDisabledSubtitle": { + "description": "Subtitle when all files access is disabled" + }, + "allFilesAccessDescription": "Enable this if you encounter write errors when saving to custom folders. Android 13+ restricts access to certain directories by default.", + "@allFilesAccessDescription": { + "description": "Description explaining when to enable all files access" + }, + "allFilesAccessDeniedMessage": "Permission was denied. Please enable 'All files access' manually in system settings.", + "@allFilesAccessDeniedMessage": { + "description": "Message when permission is permanently denied" + }, + "allFilesAccessDisabledMessage": "All Files Access disabled. The app will use limited storage access.", + "@allFilesAccessDisabledMessage": { + "description": "Snackbar message when user disables all files access" + }, + "settingsLocalLibrary": "Local Library", + "@settingsLocalLibrary": { + "description": "Settings menu item - local library" + }, + "settingsLocalLibrarySubtitle": "Scan music & detect duplicates", + "@settingsLocalLibrarySubtitle": { + "description": "Subtitle for local library settings" + }, + "settingsCache": "Storage & Cache", + "@settingsCache": { + "description": "Settings menu item - cache management" + }, + "settingsCacheSubtitle": "View size and clear cached data", + "@settingsCacheSubtitle": { + "description": "Subtitle for cache management menu" + }, + "libraryTitle": "Local Library", + "@libraryTitle": { + "description": "Library settings page title" + }, + "libraryScanSettings": "Scan Settings", + "@libraryScanSettings": { + "description": "Section header for scan settings" + }, + "libraryEnableLocalLibrary": "Enable Local Library", + "@libraryEnableLocalLibrary": { + "description": "Toggle to enable library scanning" + }, + "libraryEnableLocalLibrarySubtitle": "Scan and track your existing music", + "@libraryEnableLocalLibrarySubtitle": { + "description": "Subtitle for enable toggle" + }, + "libraryFolder": "Library Folder", + "@libraryFolder": { + "description": "Folder selection setting" + }, + "libraryFolderHint": "Tap to select folder", + "@libraryFolderHint": { + "description": "Placeholder when no folder selected" + }, + "libraryShowDuplicateIndicator": "Show Duplicate Indicator", + "@libraryShowDuplicateIndicator": { + "description": "Toggle for duplicate indicator in search" + }, + "libraryShowDuplicateIndicatorSubtitle": "Show when searching for existing tracks", + "@libraryShowDuplicateIndicatorSubtitle": { + "description": "Subtitle for duplicate indicator toggle" + }, + "libraryAutoScan": "Auto Scan", + "@libraryAutoScan": { + "description": "Setting for automatic library scanning" + }, + "libraryAutoScanSubtitle": "Automatically scan your library for new files", + "@libraryAutoScanSubtitle": { + "description": "Subtitle for auto scan setting" + }, + "libraryAutoScanOff": "Off", + "@libraryAutoScanOff": { + "description": "Auto scan disabled" + }, + "libraryAutoScanOnOpen": "Every app open", + "@libraryAutoScanOnOpen": { + "description": "Auto scan when app opens" + }, + "libraryAutoScanDaily": "Daily", + "@libraryAutoScanDaily": { + "description": "Auto scan once per day" + }, + "libraryAutoScanWeekly": "Weekly", + "@libraryAutoScanWeekly": { + "description": "Auto scan once per week" + }, + "libraryActions": "Actions", + "@libraryActions": { + "description": "Section header for library actions" + }, + "libraryScan": "Scan Library", + "@libraryScan": { + "description": "Button to start library scan" + }, + "libraryScanSubtitle": "Scan for audio files", + "@libraryScanSubtitle": { + "description": "Subtitle for scan button" + }, + "libraryScanSelectFolderFirst": "Select a folder first", + "@libraryScanSelectFolderFirst": { + "description": "Message when trying to scan without folder" + }, + "libraryCleanupMissingFiles": "Cleanup Missing Files", + "@libraryCleanupMissingFiles": { + "description": "Button to remove entries for missing files" + }, + "libraryCleanupMissingFilesSubtitle": "Remove entries for files that no longer exist", + "@libraryCleanupMissingFilesSubtitle": { + "description": "Subtitle for cleanup button" + }, + "libraryClear": "Clear Library", + "@libraryClear": { + "description": "Button to clear all library entries" + }, + "libraryClearSubtitle": "Remove all scanned tracks", + "@libraryClearSubtitle": { + "description": "Subtitle for clear button" + }, + "libraryClearConfirmTitle": "Clear Library", + "@libraryClearConfirmTitle": { + "description": "Dialog title for clear confirmation" + }, + "libraryClearConfirmMessage": "This will remove all scanned tracks from your library. Your actual music files will not be deleted.", + "@libraryClearConfirmMessage": { + "description": "Dialog message for clear confirmation" + }, + "libraryAbout": "About Local Library", + "@libraryAbout": { + "description": "Section header for about info" + }, + "libraryAboutDescription": "Scans your existing music collection to detect duplicates when downloading. Supports FLAC, M4A, MP3, Opus, and OGG formats. Metadata is read from file tags when available.", + "@libraryAboutDescription": { + "description": "Description of local library feature" + }, + "libraryTracksUnit": "{count, plural, =1{track} other{tracks}}", + "@libraryTracksUnit": { + "description": "Unit label for tracks count (without the number itself)", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "libraryFilesUnit": "{count, plural, =1{file} other{files}}", + "@libraryFilesUnit": { + "description": "Unit label for files count during library scanning", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "libraryLastScanned": "Last scanned: {time}", + "@libraryLastScanned": { + "description": "Last scan time display", + "placeholders": { + "time": { + "type": "String" + } + } + }, + "libraryLastScannedNever": "Never", + "@libraryLastScannedNever": { + "description": "Shown when library has never been scanned" + }, + "libraryScanning": "Scanning...", + "@libraryScanning": { + "description": "Status during scan" + }, + "libraryScanFinalizing": "Finalizing library...", + "@libraryScanFinalizing": { + "description": "Status shown after file scanning finishes but library persistence is still running" + }, + "libraryScanProgress": "{progress}% of {total} files", + "@libraryScanProgress": { + "description": "Scan progress display", + "placeholders": { + "progress": { + "type": "String" + }, + "total": { + "type": "int" + } + } + }, + "libraryInLibrary": "In Library", + "@libraryInLibrary": { + "description": "Badge shown on tracks that exist in local library" + }, + "libraryRemovedMissingFiles": "Removed {count} missing files from library", + "@libraryRemovedMissingFiles": { + "description": "Snackbar after cleanup", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "libraryCleared": "Library cleared", + "@libraryCleared": { + "description": "Snackbar after clearing library" + }, + "libraryStorageAccessRequired": "Storage Access Required", + "@libraryStorageAccessRequired": { + "description": "Dialog title for storage permission" + }, + "libraryStorageAccessMessage": "SpotiFLAC needs storage access to scan your music library. Please grant permission in settings.", + "@libraryStorageAccessMessage": { + "description": "Dialog message for storage permission" + }, + "libraryFolderNotExist": "Selected folder does not exist", + "@libraryFolderNotExist": { + "description": "Error when folder doesn't exist" + }, + "librarySourceDownloaded": "Downloaded", + "@librarySourceDownloaded": { + "description": "Badge for tracks downloaded via SpotiFLAC" + }, + "librarySourceLocal": "Local", + "@librarySourceLocal": { + "description": "Badge for tracks from local library scan" + }, + "libraryFilterAll": "All", + "@libraryFilterAll": { + "description": "Filter chip - show all library items" + }, + "libraryFilterDownloaded": "Downloaded", + "@libraryFilterDownloaded": { + "description": "Filter chip - show only downloaded items" + }, + "libraryFilterLocal": "Local", + "@libraryFilterLocal": { + "description": "Filter chip - show only local library items" + }, + "libraryFilterTitle": "Filters", + "@libraryFilterTitle": { + "description": "Filter bottom sheet title" + }, + "libraryFilterReset": "Reset", + "@libraryFilterReset": { + "description": "Reset all filters button" + }, + "libraryFilterApply": "Apply", + "@libraryFilterApply": { + "description": "Apply filters button" + }, + "libraryFilterSource": "Source", + "@libraryFilterSource": { + "description": "Filter section - source type" + }, + "libraryFilterQuality": "Quality", + "@libraryFilterQuality": { + "description": "Filter section - audio quality" + }, + "libraryFilterQualityHiRes": "Hi-Res (24bit)", + "@libraryFilterQualityHiRes": { + "description": "Filter option - high resolution audio" + }, + "libraryFilterQualityCD": "CD (16bit)", + "@libraryFilterQualityCD": { + "description": "Filter option - CD quality audio" + }, + "libraryFilterQualityLossy": "Lossy", + "@libraryFilterQualityLossy": { + "description": "Filter option - lossy compressed audio" + }, + "libraryFilterFormat": "Format", + "@libraryFilterFormat": { + "description": "Filter section - file format" + }, + "libraryFilterMetadata": "Metadata", + "@libraryFilterMetadata": { + "description": "Filter section - metadata completeness" + }, + "libraryFilterMetadataComplete": "Complete metadata", + "@libraryFilterMetadataComplete": { + "description": "Filter option - items with complete metadata" + }, + "libraryFilterMetadataMissingAny": "Missing any metadata", + "@libraryFilterMetadataMissingAny": { + "description": "Filter option - items missing any tracked metadata field" + }, + "libraryFilterMetadataMissingYear": "Missing year", + "@libraryFilterMetadataMissingYear": { + "description": "Filter option - items missing release year/date" + }, + "libraryFilterMetadataMissingGenre": "Missing genre", + "@libraryFilterMetadataMissingGenre": { + "description": "Filter option - items missing genre" + }, + "libraryFilterMetadataMissingAlbumArtist": "Missing album artist", + "@libraryFilterMetadataMissingAlbumArtist": { + "description": "Filter option - items missing album artist" + }, + "libraryFilterSort": "Sort", + "@libraryFilterSort": { + "description": "Filter section - sort order" + }, + "libraryFilterSortLatest": "Latest", + "@libraryFilterSortLatest": { + "description": "Sort option - newest first" + }, + "libraryFilterSortOldest": "Oldest", + "@libraryFilterSortOldest": { + "description": "Sort option - oldest first" + }, + "libraryFilterSortAlbumAsc": "Album (A-Z)", + "@libraryFilterSortAlbumAsc": { + "description": "Sort option - album ascending" + }, + "libraryFilterSortAlbumDesc": "Album (Z-A)", + "@libraryFilterSortAlbumDesc": { + "description": "Sort option - album descending" + }, + "libraryFilterSortGenreAsc": "Genre (A-Z)", + "@libraryFilterSortGenreAsc": { + "description": "Sort option - genre ascending" + }, + "libraryFilterSortGenreDesc": "Genre (Z-A)", + "@libraryFilterSortGenreDesc": { + "description": "Sort option - genre descending" + }, + "timeJustNow": "Just now", + "@timeJustNow": { + "description": "Relative time - less than a minute ago" + }, + "timeMinutesAgo": "{count, plural, =1{1 minute ago} other{{count} minutes ago}}", + "@timeMinutesAgo": { + "description": "Relative time - minutes ago", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "timeHoursAgo": "{count, plural, =1{1 hour ago} other{{count} hours ago}}", + "@timeHoursAgo": { + "description": "Relative time - hours ago", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "tutorialWelcomeTitle": "Welcome to SpotiFLAC!", + "@tutorialWelcomeTitle": { + "description": "Tutorial welcome page title" + }, + "tutorialWelcomeDesc": "Let's learn how to download your favorite music in lossless quality. This quick tutorial will show you the basics.", + "@tutorialWelcomeDesc": { + "description": "Tutorial welcome page description" + }, + "tutorialWelcomeTip1": "Download music from Spotify, Deezer, or paste any supported URL", + "@tutorialWelcomeTip1": { + "description": "Tutorial welcome tip 1" + }, + "tutorialWelcomeTip2": "Get FLAC quality audio from Tidal, Qobuz, or Deezer", + "@tutorialWelcomeTip2": { + "description": "Tutorial welcome tip 2" + }, + "tutorialWelcomeTip3": "Automatic metadata, cover art, and lyrics embedding", + "@tutorialWelcomeTip3": { + "description": "Tutorial welcome tip 3" + }, + "tutorialSearchTitle": "Finding Music", + "@tutorialSearchTitle": { + "description": "Tutorial search page title" + }, + "tutorialSearchDesc": "There are two easy ways to find music you want to download.", + "@tutorialSearchDesc": { + "description": "Tutorial search page description" + }, + "tutorialDownloadTitle": "Downloading Music", + "@tutorialDownloadTitle": { + "description": "Tutorial download page title" + }, + "tutorialDownloadDesc": "Downloading music is simple and fast. Here's how it works.", + "@tutorialDownloadDesc": { + "description": "Tutorial download page description" + }, + "tutorialLibraryTitle": "Your Library", + "@tutorialLibraryTitle": { + "description": "Tutorial library page title" + }, + "tutorialLibraryDesc": "All your downloaded music is organized in the Library tab.", + "@tutorialLibraryDesc": { + "description": "Tutorial library page description" + }, + "tutorialLibraryTip1": "View download progress and queue in the Library tab", + "@tutorialLibraryTip1": { + "description": "Tutorial library tip 1" + }, + "tutorialLibraryTip2": "Tap any track to play it with your music player", + "@tutorialLibraryTip2": { + "description": "Tutorial library tip 2" + }, + "tutorialLibraryTip3": "Switch between list and grid view for better browsing", + "@tutorialLibraryTip3": { + "description": "Tutorial library tip 3" + }, + "tutorialExtensionsTitle": "Extensions", + "@tutorialExtensionsTitle": { + "description": "Tutorial extensions page title" + }, + "tutorialExtensionsDesc": "Extend the app's capabilities with community extensions.", + "@tutorialExtensionsDesc": { + "description": "Tutorial extensions page description" + }, + "tutorialExtensionsTip1": "Browse the Repo tab to discover useful extensions", + "@tutorialExtensionsTip1": { + "description": "Tutorial extensions tip 1" + }, + "tutorialExtensionsTip2": "Add new download providers or search sources", + "@tutorialExtensionsTip2": { + "description": "Tutorial extensions tip 2" + }, + "tutorialExtensionsTip3": "Get lyrics, enhanced metadata, and more features", + "@tutorialExtensionsTip3": { + "description": "Tutorial extensions tip 3" + }, + "tutorialSettingsTitle": "Customize Your Experience", + "@tutorialSettingsTitle": { + "description": "Tutorial settings page title" + }, + "tutorialSettingsDesc": "Personalize the app in Settings to match your preferences.", + "@tutorialSettingsDesc": { + "description": "Tutorial settings page description" + }, + "tutorialSettingsTip1": "Change download location and folder organization", + "@tutorialSettingsTip1": { + "description": "Tutorial settings tip 1" + }, + "tutorialSettingsTip2": "Set default audio quality and format preferences", + "@tutorialSettingsTip2": { + "description": "Tutorial settings tip 2" + }, + "tutorialSettingsTip3": "Customize app theme and appearance", + "@tutorialSettingsTip3": { + "description": "Tutorial settings tip 3" + }, + "tutorialReadyMessage": "You're all set! Start downloading your favorite music now.", + "@tutorialReadyMessage": { + "description": "Tutorial completion message" + }, + "libraryForceFullScan": "Force Full Scan", + "@libraryForceFullScan": { + "description": "Button to force a complete rescan of library" + }, + "libraryForceFullScanSubtitle": "Rescan all files, ignoring cache", + "@libraryForceFullScanSubtitle": { + "description": "Subtitle for force full scan button" + }, + "cleanupOrphanedDownloads": "Cleanup Orphaned Downloads", + "@cleanupOrphanedDownloads": { + "description": "Button to remove history entries for deleted files" + }, + "cleanupOrphanedDownloadsSubtitle": "Remove history entries for files that no longer exist", + "@cleanupOrphanedDownloadsSubtitle": { + "description": "Subtitle for orphaned cleanup button" + }, + "cleanupOrphanedDownloadsResult": "Removed {count} orphaned entries from history", + "@cleanupOrphanedDownloadsResult": { + "description": "Snackbar after orphan cleanup", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "cleanupOrphanedDownloadsNone": "No orphaned entries found", + "@cleanupOrphanedDownloadsNone": { + "description": "Snackbar when no orphans found" + }, + "cacheTitle": "Storage & Cache", + "@cacheTitle": { + "description": "Cache management page title" + }, + "cacheSummaryTitle": "Cache overview", + "@cacheSummaryTitle": { + "description": "Heading for cache summary card" + }, + "cacheSummarySubtitle": "Clearing cache will not remove downloaded music files.", + "@cacheSummarySubtitle": { + "description": "Helper text for cache summary card" + }, + "cacheEstimatedTotal": "Estimated cache usage: {size}", + "@cacheEstimatedTotal": { + "description": "Total cache size shown in summary", + "placeholders": { + "size": { + "type": "String" + } + } + }, + "cacheSectionStorage": "Cached Data", + "@cacheSectionStorage": { + "description": "Section header for cache entries" + }, + "cacheSectionMaintenance": "Maintenance", + "@cacheSectionMaintenance": { + "description": "Section header for cleanup actions" + }, + "cacheAppDirectory": "App cache directory", + "@cacheAppDirectory": { + "description": "Cache item title for app cache directory" + }, + "cacheAppDirectoryDesc": "HTTP responses, WebView data, and other temporary app data.", + "@cacheAppDirectoryDesc": { + "description": "Description of what app cache directory contains" + }, + "cacheTempDirectory": "Temporary directory", + "@cacheTempDirectory": { + "description": "Cache item title for temporary files directory" + }, + "cacheTempDirectoryDesc": "Temporary files from downloads and audio conversion.", + "@cacheTempDirectoryDesc": { + "description": "Description of what temporary directory contains" + }, + "cacheCoverImage": "Cover image cache", + "@cacheCoverImage": { + "description": "Cache item title for persistent cover images" + }, + "cacheCoverImageDesc": "Downloaded album and track cover art. Will re-download when viewed.", + "@cacheCoverImageDesc": { + "description": "Description of what cover image cache contains" + }, + "cacheLibraryCover": "Library cover cache", + "@cacheLibraryCover": { + "description": "Cache item title for local library cover art images" + }, + "cacheLibraryCoverDesc": "Cover art extracted from local music files. Will re-extract on next scan.", + "@cacheLibraryCoverDesc": { + "description": "Description of what library cover cache contains" + }, + "cacheExploreFeed": "Explore feed cache", + "@cacheExploreFeed": { + "description": "Cache item title for explore home feed cache" + }, + "cacheExploreFeedDesc": "Explore tab content (new releases, trending). Will refresh on next visit.", + "@cacheExploreFeedDesc": { + "description": "Description of what explore feed cache contains" + }, + "cacheTrackLookup": "Track lookup cache", + "@cacheTrackLookup": { + "description": "Cache item title for track ID lookup cache" + }, + "cacheTrackLookupDesc": "Spotify/Deezer track ID lookups. Clearing may slow next few searches.", + "@cacheTrackLookupDesc": { + "description": "Description of what track lookup cache contains" + }, + "cacheCleanupUnusedDesc": "Remove orphaned download history and library entries for missing files.", + "@cacheCleanupUnusedDesc": { + "description": "Description of what cleanup unused data does" + }, + "cacheNoData": "No cached data", + "@cacheNoData": { + "description": "Label when cache category has no data" + }, + "cacheSizeWithFiles": "{size} in {count} files", + "@cacheSizeWithFiles": { + "description": "Cache size and file count", + "placeholders": { + "size": { + "type": "String" + }, + "count": { + "type": "int" + } + } + }, + "cacheSizeOnly": "{size}", + "@cacheSizeOnly": { + "description": "Cache size only", + "placeholders": { + "size": { + "type": "String" + } + } + }, + "cacheEntries": "{count} entries", + "@cacheEntries": { + "description": "Track cache entry count", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "cacheClearSuccess": "Cleared: {target}", + "@cacheClearSuccess": { + "description": "Snackbar after clearing selected cache", + "placeholders": { + "target": { + "type": "String" + } + } + }, + "cacheClearConfirmTitle": "Clear cache?", + "@cacheClearConfirmTitle": { + "description": "Dialog title before clearing one cache category" + }, + "cacheClearConfirmMessage": "This will clear cached data for {target}. Downloaded music files will not be deleted.", + "@cacheClearConfirmMessage": { + "description": "Dialog message before clearing selected cache", + "placeholders": { + "target": { + "type": "String" + } + } + }, + "cacheClearAllConfirmTitle": "Clear all cache?", + "@cacheClearAllConfirmTitle": { + "description": "Dialog title before clearing all caches" + }, + "cacheClearAllConfirmMessage": "This will clear all cache categories on this page. Downloaded music files will not be deleted.", + "@cacheClearAllConfirmMessage": { + "description": "Dialog message before clearing all caches" + }, + "cacheClearAll": "Clear all cache", + "@cacheClearAll": { + "description": "Button label to clear all caches" + }, + "cacheCleanupUnused": "Cleanup unused data", + "@cacheCleanupUnused": { + "description": "Action title for cleaning unused entries" + }, + "cacheCleanupUnusedSubtitle": "Remove orphaned download history and missing library entries", + "@cacheCleanupUnusedSubtitle": { + "description": "Subtitle for cleanup unused data action" + }, + "cacheCleanupResult": "Cleanup completed: {downloadCount} orphaned downloads, {libraryCount} missing library entries", + "@cacheCleanupResult": { + "description": "Snackbar after unused data cleanup", + "placeholders": { + "downloadCount": { + "type": "int" + }, + "libraryCount": { + "type": "int" + } + } + }, + "cacheRefreshStats": "Refresh stats", + "@cacheRefreshStats": { + "description": "Button label to refresh cache statistics" + }, + "trackSaveCoverArt": "Save Cover Art", + "@trackSaveCoverArt": { + "description": "Menu action - save album cover art as file" + }, + "trackSaveCoverArtSubtitle": "Save album art as .jpg file", + "@trackSaveCoverArtSubtitle": { + "description": "Subtitle for save cover art action" + }, + "trackSaveLyrics": "Save Lyrics (.lrc)", + "@trackSaveLyrics": { + "description": "Menu action - save lyrics as .lrc file" + }, + "trackSaveLyricsSubtitle": "Fetch and save lyrics as .lrc file", + "@trackSaveLyricsSubtitle": { + "description": "Subtitle for save lyrics action" + }, + "trackSaveLyricsProgress": "Saving lyrics...", + "@trackSaveLyricsProgress": { + "description": "Snackbar while saving lyrics to file" + }, + "trackReEnrich": "Re-enrich", + "@trackReEnrich": { + "description": "Menu action - re-embed metadata into audio file" + }, + "trackReEnrichOnlineSubtitle": "Search metadata online and embed into file", + "@trackReEnrichOnlineSubtitle": { + "description": "Subtitle for re-enrich metadata action for local items" + }, + "trackReEnrichFieldsTitle": "Fields to update", + "@trackReEnrichFieldsTitle": { + "description": "Section title for field selection in re-enrich dialog" + }, + "trackReEnrichFieldCover": "Cover Art", + "@trackReEnrichFieldCover": { + "description": "Checkbox label for cover art field in re-enrich" + }, + "trackReEnrichFieldLyrics": "Lyrics", + "@trackReEnrichFieldLyrics": { + "description": "Checkbox label for lyrics field in re-enrich" + }, + "trackReEnrichFieldBasicTags": "Album, Album Artist", + "@trackReEnrichFieldBasicTags": { + "description": "Checkbox label for basic tags in re-enrich (title/artist are never overwritten)" + }, + "trackReEnrichFieldTrackInfo": "Track & Disc Number", + "@trackReEnrichFieldTrackInfo": { + "description": "Checkbox label for track info in re-enrich" + }, + "trackReEnrichFieldReleaseInfo": "Date & ISRC", + "@trackReEnrichFieldReleaseInfo": { + "description": "Checkbox label for release info in re-enrich" + }, + "trackReEnrichFieldExtra": "Genre, Label, Copyright", + "@trackReEnrichFieldExtra": { + "description": "Checkbox label for extra metadata in re-enrich" + }, + "trackReEnrichSelectAll": "Select All", + "@trackReEnrichSelectAll": { + "description": "Select all fields checkbox in re-enrich" + }, + "trackEditMetadata": "Edit Metadata", + "@trackEditMetadata": { + "description": "Menu action - edit embedded metadata" + }, + "trackCoverSaved": "Cover art saved to {fileName}", + "@trackCoverSaved": { + "description": "Snackbar after cover art saved", + "placeholders": { + "fileName": { + "type": "String" + } + } + }, + "trackCoverNoSource": "No cover art source available", + "@trackCoverNoSource": { + "description": "Snackbar when no cover art URL or embedded cover" + }, + "trackLyricsSaved": "Lyrics saved to {fileName}", + "@trackLyricsSaved": { + "description": "Snackbar after lyrics saved", + "placeholders": { + "fileName": { + "type": "String" + } + } + }, + "trackReEnrichProgress": "Re-enriching metadata...", + "@trackReEnrichProgress": { + "description": "Snackbar while re-enriching metadata" + }, + "trackReEnrichSearching": "Searching metadata online...", + "@trackReEnrichSearching": { + "description": "Snackbar while searching metadata from internet for local items" + }, + "trackReEnrichSuccess": "Metadata re-enriched successfully", + "@trackReEnrichSuccess": { + "description": "Snackbar after successful re-enrichment" + }, + "trackReEnrichFfmpegFailed": "FFmpeg metadata embed failed", + "@trackReEnrichFfmpegFailed": { + "description": "Snackbar when FFmpeg embed fails for MP3/Opus" + }, + "queueFlacAction": "Queue FLAC", + "@queueFlacAction": { + "description": "Action/button label for queueing FLAC redownloads for local tracks" + }, + "queueFlacConfirmMessage": "Search online matches for the selected tracks and queue FLAC downloads.\n\nExisting files will not be modified or deleted.\n\nOnly high-confidence matches are queued automatically.\n\n{count} selected", + "@queueFlacConfirmMessage": { + "description": "Confirmation dialog body before queueing FLAC redownloads for local tracks", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "queueFlacFindingProgress": "Finding FLAC matches... ({current}/{total})", + "@queueFlacFindingProgress": { + "description": "Snackbar while resolving remote matches for local FLAC redownloads", + "placeholders": { + "current": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "queueFlacNoReliableMatches": "No reliable online matches found for the selection", + "@queueFlacNoReliableMatches": { + "description": "Snackbar when no safe FLAC redownload matches were found" + }, + "queueFlacQueuedWithSkipped": "Added {addedCount} tracks to queue, skipped {skippedCount}", + "@queueFlacQueuedWithSkipped": { + "description": "Snackbar when some selected local tracks were queued for FLAC redownload and some were skipped", + "placeholders": { + "addedCount": { + "type": "int" + }, + "skippedCount": { + "type": "int" + } + } + }, + "trackSaveFailed": "Failed: {error}", + "@trackSaveFailed": { + "description": "Snackbar when save operation fails", + "placeholders": { + "error": { + "type": "String" + } + } + }, + "trackConvertFormat": "Convert Format", + "@trackConvertFormat": { + "description": "Menu item - convert audio format" + }, + "trackConvertFormatSubtitle": "Convert to MP3, Opus, ALAC, or FLAC", + "@trackConvertFormatSubtitle": { + "description": "Subtitle for convert format menu item" + }, + "trackConvertTitle": "Convert Audio", + "@trackConvertTitle": { + "description": "Title of convert bottom sheet" + }, + "trackConvertTargetFormat": "Target Format", + "@trackConvertTargetFormat": { + "description": "Label for format selection" + }, + "trackConvertBitrate": "Bitrate", + "@trackConvertBitrate": { + "description": "Label for bitrate selection" + }, + "trackConvertConfirmTitle": "Confirm Conversion", + "@trackConvertConfirmTitle": { + "description": "Confirmation dialog title" + }, + "trackConvertConfirmMessage": "Convert from {sourceFormat} to {targetFormat} at {bitrate}?\n\nThe original file will be deleted after conversion.", + "@trackConvertConfirmMessage": { + "description": "Confirmation dialog message", + "placeholders": { + "sourceFormat": { + "type": "String" + }, + "targetFormat": { + "type": "String" + }, + "bitrate": { + "type": "String" + } + } + }, + "trackConvertConfirmMessageLossless": "Convert from {sourceFormat} to {targetFormat}? (Lossless — no quality loss)\n\nThe original file will be deleted after conversion.", + "@trackConvertConfirmMessageLossless": { + "description": "Confirmation dialog message for lossless-to-lossless conversion", + "placeholders": { + "sourceFormat": { + "type": "String" + }, + "targetFormat": { + "type": "String" + } + } + }, + "trackConvertLosslessHint": "Lossless conversion — no quality loss", + "@trackConvertLosslessHint": { + "description": "Hint shown when converting between lossless formats" + }, + "trackConvertConverting": "Converting audio...", + "@trackConvertConverting": { + "description": "Snackbar while converting" + }, + "trackConvertSuccess": "Converted to {format} successfully", + "@trackConvertSuccess": { + "description": "Snackbar after successful conversion", + "placeholders": { + "format": { + "type": "String" + } + } + }, + "trackConvertFailed": "Conversion failed", + "@trackConvertFailed": { + "description": "Snackbar when conversion fails" + }, + "cueSplitTitle": "Split CUE Sheet", + "@cueSplitTitle": { + "description": "Title for CUE split bottom sheet" + }, + "cueSplitSubtitle": "Split CUE+FLAC into individual tracks", + "@cueSplitSubtitle": { + "description": "Subtitle for CUE split menu item" + }, + "cueSplitAlbum": "Album: {album}", + "@cueSplitAlbum": { + "description": "Album name in CUE split sheet", + "placeholders": { + "album": { + "type": "String" + } + } + }, + "cueSplitArtist": "Artist: {artist}", + "@cueSplitArtist": { + "description": "Artist name in CUE split sheet", + "placeholders": { + "artist": { + "type": "String" + } + } + }, + "cueSplitTrackCount": "{count} tracks", + "@cueSplitTrackCount": { + "description": "Number of tracks in CUE sheet", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "cueSplitConfirmTitle": "Split CUE Album", + "@cueSplitConfirmTitle": { + "description": "CUE split confirmation dialog title" + }, + "cueSplitConfirmMessage": "Split \"{album}\" into {count} individual FLAC files?\n\nFiles will be saved to the same directory.", + "@cueSplitConfirmMessage": { + "description": "CUE split confirmation dialog message", + "placeholders": { + "album": { + "type": "String" + }, + "count": { + "type": "int" + } + } + }, + "cueSplitSplitting": "Splitting CUE sheet... ({current}/{total})", + "@cueSplitSplitting": { + "description": "Snackbar while splitting CUE", + "placeholders": { + "current": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "cueSplitSuccess": "Split into {count} tracks successfully", + "@cueSplitSuccess": { + "description": "Snackbar after successful CUE split", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "cueSplitFailed": "CUE split failed", + "@cueSplitFailed": { + "description": "Snackbar when CUE split fails" + }, + "cueSplitNoAudioFile": "Audio file not found for this CUE sheet", + "@cueSplitNoAudioFile": { + "description": "Error when CUE audio file is missing" + }, + "cueSplitButton": "Split into Tracks", + "@cueSplitButton": { + "description": "Button text to start CUE splitting" + }, + "actionCreate": "Create", + "@actionCreate": { + "description": "Generic action button - create" + }, + "collectionFoldersTitle": "My folders", + "@collectionFoldersTitle": { + "description": "Library section title for custom folders" + }, + "collectionWishlist": "Wishlist", + "@collectionWishlist": { + "description": "Custom folder for saved tracks to download later" + }, + "collectionLoved": "Loved", + "@collectionLoved": { + "description": "Custom folder for favorite tracks" + }, + "collectionPlaylists": "Playlists", + "@collectionPlaylists": { + "description": "Custom user playlists folder" + }, + "collectionPlaylist": "Playlist", + "@collectionPlaylist": { + "description": "Single playlist label" + }, + "collectionAddToPlaylist": "Add to playlist", + "@collectionAddToPlaylist": { + "description": "Action to add a track to user playlist" + }, + "collectionCreatePlaylist": "Create playlist", + "@collectionCreatePlaylist": { + "description": "Action to create a new playlist" + }, + "collectionNoPlaylistsYet": "No playlists yet", + "@collectionNoPlaylistsYet": { + "description": "Empty state title when user has no playlists" + }, + "collectionNoPlaylistsSubtitle": "Create a playlist to start categorizing tracks", + "@collectionNoPlaylistsSubtitle": { + "description": "Empty state subtitle when user has no playlists" + }, + "collectionPlaylistTracks": "{count, plural, =1{1 track} other{{count} tracks}}", + "@collectionPlaylistTracks": { + "description": "Track count label for custom playlists", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "collectionAddedToPlaylist": "Added to \"{playlistName}\"", + "@collectionAddedToPlaylist": { + "description": "Snackbar after adding track to playlist", + "placeholders": { + "playlistName": { + "type": "String" + } + } + }, + "collectionAlreadyInPlaylist": "Already in \"{playlistName}\"", + "@collectionAlreadyInPlaylist": { + "description": "Snackbar when track already exists in playlist", + "placeholders": { + "playlistName": { + "type": "String" + } + } + }, + "collectionPlaylistCreated": "Playlist created", + "@collectionPlaylistCreated": { + "description": "Snackbar after creating playlist" + }, + "collectionPlaylistNameHint": "Playlist name", + "@collectionPlaylistNameHint": { + "description": "Hint text for playlist name input" + }, + "collectionPlaylistNameRequired": "Playlist name is required", + "@collectionPlaylistNameRequired": { + "description": "Validation error for empty playlist name" + }, + "collectionRenamePlaylist": "Rename playlist", + "@collectionRenamePlaylist": { + "description": "Action to rename playlist" + }, + "collectionDeletePlaylist": "Delete playlist", + "@collectionDeletePlaylist": { + "description": "Action to delete playlist" + }, + "collectionDeletePlaylistMessage": "Delete \"{playlistName}\" and all tracks inside it?", + "@collectionDeletePlaylistMessage": { + "description": "Confirmation message for deleting playlist", + "placeholders": { + "playlistName": { + "type": "String" + } + } + }, + "collectionPlaylistDeleted": "Playlist deleted", + "@collectionPlaylistDeleted": { + "description": "Snackbar after deleting playlist" + }, + "collectionPlaylistRenamed": "Playlist renamed", + "@collectionPlaylistRenamed": { + "description": "Snackbar after renaming playlist" + }, + "collectionWishlistEmptyTitle": "Wishlist is empty", + "@collectionWishlistEmptyTitle": { + "description": "Wishlist empty state title" + }, + "collectionWishlistEmptySubtitle": "Tap + on tracks to save what you want to download later", + "@collectionWishlistEmptySubtitle": { + "description": "Wishlist empty state subtitle" + }, + "collectionLovedEmptyTitle": "Loved folder is empty", + "@collectionLovedEmptyTitle": { + "description": "Loved empty state title" + }, + "collectionLovedEmptySubtitle": "Tap love on tracks to keep your favorites", + "@collectionLovedEmptySubtitle": { + "description": "Loved empty state subtitle" + }, + "collectionPlaylistEmptyTitle": "Playlist is empty", + "@collectionPlaylistEmptyTitle": { + "description": "Playlist empty state title" + }, + "collectionPlaylistEmptySubtitle": "Long-press + on any track to add it here", + "@collectionPlaylistEmptySubtitle": { + "description": "Playlist empty state subtitle" + }, + "collectionRemoveFromPlaylist": "Remove from playlist", + "@collectionRemoveFromPlaylist": { + "description": "Tooltip for removing track from playlist" + }, + "collectionRemoveFromFolder": "Remove from folder", + "@collectionRemoveFromFolder": { + "description": "Tooltip for removing track from wishlist/loved folder" + }, + "collectionRemoved": "\"{trackName}\" removed", + "@collectionRemoved": { + "description": "Snackbar after removing a track from a collection", + "placeholders": { + "trackName": { + "type": "String" + } + } + }, + "collectionAddedToLoved": "\"{trackName}\" added to Loved", + "@collectionAddedToLoved": { + "description": "Snackbar after adding track to loved folder", + "placeholders": { + "trackName": { + "type": "String" + } + } + }, + "collectionRemovedFromLoved": "\"{trackName}\" removed from Loved", + "@collectionRemovedFromLoved": { + "description": "Snackbar after removing track from loved folder", + "placeholders": { + "trackName": { + "type": "String" + } + } + }, + "collectionAddedToWishlist": "\"{trackName}\" added to Wishlist", + "@collectionAddedToWishlist": { + "description": "Snackbar after adding track to wishlist", + "placeholders": { + "trackName": { + "type": "String" + } + } + }, + "collectionRemovedFromWishlist": "\"{trackName}\" removed from Wishlist", + "@collectionRemovedFromWishlist": { + "description": "Snackbar after removing track from wishlist", + "placeholders": { + "trackName": { + "type": "String" + } + } + }, + "trackOptionAddToLoved": "Add to Loved", + "@trackOptionAddToLoved": { + "description": "Bottom sheet action label - add track to loved folder" + }, + "trackOptionRemoveFromLoved": "Remove from Loved", + "@trackOptionRemoveFromLoved": { + "description": "Bottom sheet action label - remove track from loved folder" + }, + "trackOptionAddToWishlist": "Add to Wishlist", + "@trackOptionAddToWishlist": { + "description": "Bottom sheet action label - add track to wishlist" + }, + "trackOptionRemoveFromWishlist": "Remove from Wishlist", + "@trackOptionRemoveFromWishlist": { + "description": "Bottom sheet action label - remove track from wishlist" + }, + "collectionPlaylistChangeCover": "Change cover image", + "@collectionPlaylistChangeCover": { + "description": "Bottom sheet action to pick a custom cover image for a playlist" + }, + "collectionPlaylistRemoveCover": "Remove cover image", + "@collectionPlaylistRemoveCover": { + "description": "Bottom sheet action to remove custom cover image from a playlist" + }, + "selectionShareCount": "Share {count} {count, plural, =1{track} other{tracks}}", + "@selectionShareCount": { + "description": "Share button text with count in selection mode", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "selectionShareNoFiles": "No shareable files found", + "@selectionShareNoFiles": { + "description": "Snackbar when no selected files exist on disk" + }, + "selectionConvertCount": "Convert {count} {count, plural, =1{track} other{tracks}}", + "@selectionConvertCount": { + "description": "Convert button text with count in selection mode", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "selectionConvertNoConvertible": "No convertible tracks selected", + "@selectionConvertNoConvertible": { + "description": "Snackbar when no selected tracks support conversion" + }, + "selectionBatchConvertConfirmTitle": "Batch Convert", + "@selectionBatchConvertConfirmTitle": { + "description": "Confirmation dialog title for batch conversion" + }, + "selectionBatchConvertConfirmMessage": "Convert {count} {count, plural, =1{track} other{tracks}} to {format} at {bitrate}?\n\nOriginal files will be deleted after conversion.", + "@selectionBatchConvertConfirmMessage": { + "description": "Confirmation dialog message for batch conversion", + "placeholders": { + "count": { + "type": "int" + }, + "format": { + "type": "String" + }, + "bitrate": { + "type": "String" + } + } + }, + "selectionBatchConvertConfirmMessageLossless": "Convert {count} {count, plural, =1{track} other{tracks}} to {format}? (Lossless — no quality loss)\n\nOriginal files will be deleted after conversion.", + "@selectionBatchConvertConfirmMessageLossless": { + "description": "Confirmation dialog message for lossless batch conversion", + "placeholders": { + "count": { + "type": "int" + }, + "format": { + "type": "String" + } + } + }, + "selectionBatchConvertProgress": "Converting {current} of {total}...", + "@selectionBatchConvertProgress": { + "description": "Snackbar during batch conversion progress", + "placeholders": { + "current": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "selectionBatchConvertSuccess": "Converted {success} of {total} tracks to {format}", + "@selectionBatchConvertSuccess": { + "description": "Snackbar after batch conversion completes", + "placeholders": { + "success": { + "type": "int" + }, + "total": { + "type": "int" + }, + "format": { + "type": "String" + } + } + }, + "downloadUseAlbumArtistForFoldersAlbumSubtitle": "Folder named after Album Artist tag", + "@downloadUseAlbumArtistForFoldersAlbumSubtitle": { + "description": "Subtitle when album artist is used for folder names" + }, + "downloadUseAlbumArtistForFoldersTrackSubtitle": "Folder named after Track Artist tag", + "@downloadUseAlbumArtistForFoldersTrackSubtitle": { + "description": "Subtitle when track artist is used for folder names" + }, + "lyricsProvidersTitle": "Lyrics Provider Priority", + "@lyricsProvidersTitle": { + "description": "Settings item title for lyrics provider order" + }, + "lyricsProvidersDescription": "Enable, disable and reorder lyrics sources. Providers are tried top-to-bottom until lyrics are found.", + "@lyricsProvidersDescription": { + "description": "Description on the lyrics provider priority page" + }, + "lyricsProvidersInfoText": "Extension lyrics providers always run before built-in providers. At least one provider must remain enabled.", + "@lyricsProvidersInfoText": { + "description": "Info tip on lyrics provider priority page" + }, + "lyricsProvidersEnabledSection": "Enabled ({count})", + "@lyricsProvidersEnabledSection": { + "description": "Section header for enabled providers", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "lyricsProvidersDisabledSection": "Disabled ({count})", + "@lyricsProvidersDisabledSection": { + "description": "Section header for disabled providers", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "lyricsProvidersAtLeastOne": "At least one provider must remain enabled", + "@lyricsProvidersAtLeastOne": { + "description": "Snackbar when user tries to disable the last enabled provider" + }, + "lyricsProvidersSaved": "Lyrics provider priority saved", + "@lyricsProvidersSaved": { + "description": "Snackbar after saving lyrics provider priority" + }, + "lyricsProvidersDiscardContent": "You have unsaved changes that will be lost.", + "@lyricsProvidersDiscardContent": { + "description": "Body text of the discard-changes dialog on lyrics provider page" + }, + "lyricsProviderLrclibDesc": "Open-source synced lyrics database", + "@lyricsProviderLrclibDesc": { + "description": "Description for LRCLIB provider" + }, + "lyricsProviderNeteaseDesc": "NetEase Cloud Music (good for Asian songs)", + "@lyricsProviderNeteaseDesc": { + "description": "Description for Netease provider" + }, + "lyricsProviderMusixmatchDesc": "Largest lyrics database (multi-language)", + "@lyricsProviderMusixmatchDesc": { + "description": "Description for Musixmatch provider" + }, + "lyricsProviderAppleMusicDesc": "Word-by-word synced lyrics (via proxy)", + "@lyricsProviderAppleMusicDesc": { + "description": "Description for Apple Music provider" + }, + "lyricsProviderQqMusicDesc": "QQ Music (good for Chinese songs, via proxy)", + "@lyricsProviderQqMusicDesc": { + "description": "Description for QQ Music provider" + }, + "lyricsProviderExtensionDesc": "Extension provider", + "@lyricsProviderExtensionDesc": { + "description": "Generic description for extension-based lyrics providers" + }, + "safMigrationTitle": "Storage Update Required", + "@safMigrationTitle": { + "description": "Title of SAF migration dialog" + }, + "safMigrationMessage1": "SpotiFLAC now uses Android Storage Access Framework (SAF) for downloads. This fixes \"permission denied\" errors on Android 10+.", + "@safMigrationMessage1": { + "description": "First paragraph of SAF migration dialog" + }, + "safMigrationMessage2": "Please select your download folder again to switch to the new storage system.", + "@safMigrationMessage2": { + "description": "Second paragraph of SAF migration dialog" + }, + "safMigrationSuccess": "Download folder updated to SAF mode", + "@safMigrationSuccess": { + "description": "Snackbar after successfully migrating to SAF" + }, + "settingsDonate": "Support Development", + "@settingsDonate": { + "description": "Settings menu item - donate page" + }, + "settingsDonateSubtitle": "Buy the developer a coffee", + "@settingsDonateSubtitle": { + "description": "Subtitle for donate menu item" + }, + "tooltipLoveAll": "Love All", + "@tooltipLoveAll": { + "description": "Tooltip for the Love All button on album/playlist screens" + }, + "tooltipAddToPlaylist": "Add to Playlist", + "@tooltipAddToPlaylist": { + "description": "Tooltip for the Add to Playlist button" + }, + "snackbarRemovedTracksFromLoved": "Removed {count} tracks from Loved", + "@snackbarRemovedTracksFromLoved": { + "description": "Snackbar after removing multiple tracks from Loved folder", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "snackbarAddedTracksToLoved": "Added {count} tracks to Loved", + "@snackbarAddedTracksToLoved": { + "description": "Snackbar after adding multiple tracks to Loved folder", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "dialogDownloadAllTitle": "Download All", + "@dialogDownloadAllTitle": { + "description": "Dialog title for bulk download confirmation" + }, + "dialogDownloadAllMessage": "Download {count} tracks?", + "@dialogDownloadAllMessage": { + "description": "Body of the Download All confirmation dialog", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "homeSkipAlreadyDownloaded": "Skip already downloaded songs", + "@homeSkipAlreadyDownloaded": { + "description": "Checkbox label in import dialog to skip already-downloaded songs" + }, + "homeGoToAlbum": "Go to Album", + "@homeGoToAlbum": { + "description": "Context menu item to navigate to the album page" + }, + "homeAlbumInfoUnavailable": "Album info not available", + "@homeAlbumInfoUnavailable": { + "description": "Snackbar when album info cannot be loaded" + }, + "snackbarLoadingCueSheet": "Loading CUE sheet...", + "@snackbarLoadingCueSheet": { + "description": "Snackbar while loading a CUE sheet file" + }, + "snackbarMetadataSaved": "Metadata saved successfully", + "@snackbarMetadataSaved": { + "description": "Snackbar after successfully saving track metadata" + }, + "snackbarFailedToEmbedLyrics": "Failed to embed lyrics", + "@snackbarFailedToEmbedLyrics": { + "description": "Snackbar when lyrics embedding fails" + }, + "snackbarFailedToWriteStorage": "Failed to write back to storage", + "@snackbarFailedToWriteStorage": { + "description": "Snackbar when writing metadata back to file fails" + }, + "snackbarError": "Error: {error}", + "@snackbarError": { + "description": "Generic error snackbar with error detail", + "placeholders": { + "error": { + "type": "String" + } + } + }, + "snackbarNoActionDefined": "No action defined for this button", + "@snackbarNoActionDefined": { + "description": "Snackbar when an extension button has no action configured" + }, + "noTracksFoundForAlbum": "No tracks found for this album", + "@noTracksFoundForAlbum": { + "description": "Empty state message when an album has no tracks" + }, + "downloadLocationSubtitle": "Choose where to save your downloaded tracks", + "@downloadLocationSubtitle": { + "description": "Subtitle shown in the download location picker sheet" + }, + "storageModeAppFolder": "App Folder (Recommended)", + "@storageModeAppFolder": { + "description": "Storage mode option - app-managed folder" + }, + "storageModeAppFolderSubtitle": "Saves to Music/SpotiFLAC by default", + "@storageModeAppFolderSubtitle": { + "description": "Subtitle for app folder storage mode" + }, + "storageModeSaf": "Custom Folder (SAF)", + "@storageModeSaf": { + "description": "Storage mode option - Storage Access Framework" + }, + "storageModeSafSubtitle": "Pick any folder, including SD card", + "@storageModeSafSubtitle": { + "description": "Subtitle for SAF storage mode" + }, + "downloadFilenameDescription": "Use {artist}, {title}, {album}, {track}, {year}, {date}, {disc} as placeholders.", + "@downloadFilenameDescription": { + "description": "Description shown in filename format editor" + }, + "downloadFilenameInsertTag": "Tap to insert tag:", + "@downloadFilenameInsertTag": { + "description": "Label above filename tag chips" + }, + "downloadSeparateSinglesEnabled": "Singles and EPs saved in a separate folder", + "@downloadSeparateSinglesEnabled": { + "description": "Subtitle when separate singles folder is on" + }, + "downloadSeparateSinglesDisabled": "Singles and albums saved in the same folder", + "@downloadSeparateSinglesDisabled": { + "description": "Subtitle when separate singles folder is off" + }, + "downloadArtistNameFilters": "Artist Name Filters", + "@downloadArtistNameFilters": { + "description": "Setting title for artist folder filter options" + }, + "downloadCreatePlaylistSourceFolder": "Playlist Source Folder", + "@downloadCreatePlaylistSourceFolder": { + "description": "Setting to create a subfolder per playlist source" + }, + "downloadCreatePlaylistSourceFolderEnabled": "A subfolder is created for each playlist", + "@downloadCreatePlaylistSourceFolderEnabled": { + "description": "Subtitle when playlist folder is enabled" + }, + "downloadCreatePlaylistSourceFolderDisabled": "All tracks saved directly to download folder", + "@downloadCreatePlaylistSourceFolderDisabled": { + "description": "Subtitle when playlist folder is disabled" + }, + "downloadCreatePlaylistSourceFolderRedundant": "Handled by folder organization setting", + "@downloadCreatePlaylistSourceFolderRedundant": { + "description": "Subtitle when folder organization is already set to playlist" + }, + "downloadSongLinkRegion": "SongLink Region", + "@downloadSongLinkRegion": { + "description": "Setting for SongLink region used during fallback resolution" + }, + "downloadNetworkCompatibilityMode": "Network Compatibility Mode", + "@downloadNetworkCompatibilityMode": { + "description": "Setting for legacy TLS/network handling" + }, + "downloadNetworkCompatibilityModeEnabled": "Using legacy TLS settings for older networks", + "@downloadNetworkCompatibilityModeEnabled": { + "description": "Subtitle when network compatibility mode is on" + }, + "downloadNetworkCompatibilityModeDisabled": "Using standard network settings", + "@downloadNetworkCompatibilityModeDisabled": { + "description": "Subtitle when network compatibility mode is off" + }, + "downloadSelectServiceToEnable": "Select Tidal or Qobuz to enable this option", + "@downloadSelectServiceToEnable": { + "description": "Subtitle when quality picker is disabled due to extension service" + }, + "downloadSelectTidalQobuz": "Select Tidal or Qobuz to choose audio quality", + "@downloadSelectTidalQobuz": { + "description": "Info shown when a non-built-in service is selected" + }, + "downloadEmbedLyricsDisabled": "Enable metadata embedding first", + "@downloadEmbedLyricsDisabled": { + "description": "Subtitle when lyrics embedding is blocked by metadata toggle" + }, + "downloadNeteaseIncludeTranslation": "Netease: Include Translation", + "@downloadNeteaseIncludeTranslation": { + "description": "Setting to include translated lyrics from Netease" + }, + "downloadNeteaseIncludeTranslationEnabled": "Chinese translation lines included", + "@downloadNeteaseIncludeTranslationEnabled": { + "description": "Subtitle when Netease translation is on" + }, + "downloadNeteaseIncludeTranslationDisabled": "Original lyrics only", + "@downloadNeteaseIncludeTranslationDisabled": { + "description": "Subtitle when Netease translation is off" + }, + "downloadNeteaseIncludeRomanization": "Netease: Include Romanization", + "@downloadNeteaseIncludeRomanization": { + "description": "Setting to include romanized lyrics from Netease" + }, + "downloadNeteaseIncludeRomanizationEnabled": "Romanization lines included", + "@downloadNeteaseIncludeRomanizationEnabled": { + "description": "Subtitle when Netease romanization is on" + }, + "downloadNeteaseIncludeRomanizationDisabled": "No romanization", + "@downloadNeteaseIncludeRomanizationDisabled": { + "description": "Subtitle when Netease romanization is off" + }, + "downloadAppleQqMultiPerson": "Apple / QQ: Multi-Person Lyrics", + "@downloadAppleQqMultiPerson": { + "description": "Setting for word-by-word multi-person lyrics from Apple Music and QQ Music" + }, + "downloadAppleQqMultiPersonEnabled": "Speaker labels included for duets and group tracks", + "@downloadAppleQqMultiPersonEnabled": { + "description": "Subtitle when multi-person lyrics is on" + }, + "downloadAppleQqMultiPersonDisabled": "Standard lyrics without speaker labels", + "@downloadAppleQqMultiPersonDisabled": { + "description": "Subtitle when multi-person lyrics is off" + }, + "downloadMusixmatchLanguage": "Musixmatch Language", + "@downloadMusixmatchLanguage": { + "description": "Setting for Musixmatch lyrics translation language" + }, + "downloadMusixmatchLanguageAuto": "Auto (original language)", + "@downloadMusixmatchLanguageAuto": { + "description": "Subtitle when no language is set" + }, + "downloadFilterContributing": "Filter Contributing Artists", + "@downloadFilterContributing": { + "description": "Setting to strip contributing artists from Album Artist folder name" + }, + "downloadFilterContributingEnabled": "Contributing artists removed from Album Artist folder name", + "@downloadFilterContributingEnabled": { + "description": "Subtitle when contributing artist filter is on" + }, + "downloadFilterContributingDisabled": "Full Album Artist string used", + "@downloadFilterContributingDisabled": { + "description": "Subtitle when contributing artist filter is off" + }, + "downloadProvidersNoneEnabled": "No providers enabled", + "@downloadProvidersNoneEnabled": { + "description": "Shown when no lyrics providers are active" + }, + "downloadMusixmatchLanguageCode": "Language code", + "@downloadMusixmatchLanguageCode": { + "description": "Label for Musixmatch language input field" + }, + "downloadMusixmatchLanguageHint": "e.g. en, de, ja", + "@downloadMusixmatchLanguageHint": { + "description": "Placeholder for Musixmatch language input" + }, + "downloadMusixmatchLanguageDesc": "Enter a BCP-47 language code (e.g. en, de, ja) to request translated lyrics from Musixmatch.", + "@downloadMusixmatchLanguageDesc": { + "description": "Description in Musixmatch language picker" + }, + "downloadMusixmatchAuto": "Auto", + "@downloadMusixmatchAuto": { + "description": "Button to clear Musixmatch language (use auto)" + }, + "downloadNetworkAnySubtitle": "Use WiFi or mobile data", + "@downloadNetworkAnySubtitle": { + "description": "Subtitle for any-network option in picker" + }, + "downloadNetworkWifiOnlySubtitle": "Downloads pause when on mobile data", + "@downloadNetworkWifiOnlySubtitle": { + "description": "Subtitle for WiFi-only option in picker" + }, + "downloadSongLinkRegionDesc": "Region used when resolving track links via SongLink. Choose the country where your streaming services are available.", + "@downloadSongLinkRegionDesc": { + "description": "Description in SongLink region picker" + }, + "snackbarUnsupportedAudioFormat": "Unsupported audio format", + "@snackbarUnsupportedAudioFormat": { + "description": "Snackbar when the audio format is not supported for the requested operation" + }, + "cacheRefresh": "Refresh", + "@cacheRefresh": { + "description": "Tooltip for refresh button on cache management page" + }, + "dialogDownloadPlaylistsMessage": "Download {trackCount} {trackCount, plural, =1{track} other{tracks}} from {playlistCount} {playlistCount, plural, =1{playlist} other{playlists}}?", + "@dialogDownloadPlaylistsMessage": { + "description": "Dialog message for bulk playlist download confirmation", + "placeholders": { + "trackCount": { + "type": "int" + }, + "playlistCount": { + "type": "int" + } + } + }, + "bulkDownloadPlaylistsButton": "Download {count} {count, plural, =1{playlist} other{playlists}}", + "@bulkDownloadPlaylistsButton": { + "description": "Button label for bulk downloading selected playlists", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "bulkDownloadSelectPlaylists": "Select playlists to download", + "@bulkDownloadSelectPlaylists": { + "description": "Button label when no playlists are selected for download" + }, + "snackbarSelectedPlaylistsEmpty": "Selected playlists have no tracks", + "@snackbarSelectedPlaylistsEmpty": { + "description": "Snackbar when selected playlists contain no tracks" + }, + "playlistsCount": "{count, plural, =1{1 playlist} other{{count} playlists}}", + "@playlistsCount": { + "description": "Playlist count display", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "editMetadataAutoFill": "Auto-fill from online", + "@editMetadataAutoFill": { + "description": "Section title for selective online metadata auto-fill in the edit metadata sheet" + }, + "editMetadataAutoFillDesc": "Select fields to fill automatically from online metadata", + "@editMetadataAutoFillDesc": { + "description": "Description for the auto-fill section" + }, + "editMetadataAutoFillFetch": "Fetch & Fill", + "@editMetadataAutoFillFetch": { + "description": "Button label to fetch online metadata and fill selected fields" + }, + "editMetadataAutoFillSearching": "Searching online...", + "@editMetadataAutoFillSearching": { + "description": "Snackbar shown while searching for online metadata" + }, + "editMetadataAutoFillNoResults": "No matching metadata found online", + "@editMetadataAutoFillNoResults": { + "description": "Snackbar when online metadata search returns no results" + }, + "editMetadataAutoFillDone": "Filled {count} {count, plural, =1{field} other{fields}} from online metadata", + "@editMetadataAutoFillDone": { + "description": "Snackbar confirming how many fields were auto-filled", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "editMetadataAutoFillNoneSelected": "Select at least one field to auto-fill", + "@editMetadataAutoFillNoneSelected": { + "description": "Snackbar when user taps Fetch without selecting any fields" + }, + "editMetadataFieldTitle": "Title", + "@editMetadataFieldTitle": { + "description": "Chip label for title field in auto-fill selector" + }, + "editMetadataFieldArtist": "Artist", + "@editMetadataFieldArtist": { + "description": "Chip label for artist field in auto-fill selector" + }, + "editMetadataFieldAlbum": "Album", + "@editMetadataFieldAlbum": { + "description": "Chip label for album field in auto-fill selector" + }, + "editMetadataFieldAlbumArtist": "Album Artist", + "@editMetadataFieldAlbumArtist": { + "description": "Chip label for album artist field in auto-fill selector" + }, + "editMetadataFieldDate": "Date", + "@editMetadataFieldDate": { + "description": "Chip label for date field in auto-fill selector" + }, + "editMetadataFieldTrackNum": "Track #", + "@editMetadataFieldTrackNum": { + "description": "Chip label for track number field in auto-fill selector" + }, + "editMetadataFieldDiscNum": "Disc #", + "@editMetadataFieldDiscNum": { + "description": "Chip label for disc number field in auto-fill selector" + }, + "editMetadataFieldGenre": "Genre", + "@editMetadataFieldGenre": { + "description": "Chip label for genre field in auto-fill selector" + }, + "editMetadataFieldIsrc": "ISRC", + "@editMetadataFieldIsrc": { + "description": "Chip label for ISRC field in auto-fill selector" + }, + "editMetadataFieldLabel": "Label", + "@editMetadataFieldLabel": { + "description": "Chip label for label field in auto-fill selector" + }, + "editMetadataFieldCopyright": "Copyright", + "@editMetadataFieldCopyright": { + "description": "Chip label for copyright field in auto-fill selector" + }, + "editMetadataFieldCover": "Cover Art", + "@editMetadataFieldCover": { + "description": "Chip label for cover art field in auto-fill selector" + }, + "editMetadataSelectAll": "All", + "@editMetadataSelectAll": { + "description": "Button to select all fields for auto-fill" + }, + "editMetadataSelectEmpty": "Empty only", + "@editMetadataSelectEmpty": { + "description": "Button to select only fields that are currently empty" + }, + "queueDownloadingCount": "Downloading ({count})", + "@queueDownloadingCount": { + "description": "Header for active downloads section with count", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "queueDownloadedHeader": "Downloaded", + "@queueDownloadedHeader": { + "description": "Header label for downloaded items section in library" + }, + "queueFilteringIndicator": "Filtering...", + "@queueFilteringIndicator": { + "description": "Shown while filter results are being computed" + }, + "queueTrackCount": "{count, plural, =1{1 track} other{{count} tracks}}", + "@queueTrackCount": { + "description": "Track count label with plural support", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "queueAlbumCount": "{count, plural, =1{1 album} other{{count} albums}}", + "@queueAlbumCount": { + "description": "Album count label with plural support", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "queueEmptyAlbums": "No album downloads", + "@queueEmptyAlbums": { + "description": "Empty state title when no album downloads exist" + }, + "queueEmptyAlbumsSubtitle": "Download multiple tracks from an album to see them here", + "@queueEmptyAlbumsSubtitle": { + "description": "Empty state subtitle for album downloads" + }, + "queueEmptySingles": "No single downloads", + "@queueEmptySingles": { + "description": "Empty state title when no single track downloads exist" + }, + "queueEmptySinglesSubtitle": "Single track downloads will appear here", + "@queueEmptySinglesSubtitle": { + "description": "Empty state subtitle for single track downloads" + }, + "queueEmptyHistory": "No download history", + "@queueEmptyHistory": { + "description": "Empty state title when download history is empty" + }, + "queueEmptyHistorySubtitle": "Downloaded tracks will appear here", + "@queueEmptyHistorySubtitle": { + "description": "Empty state subtitle for download history" + }, + "selectionAllPlaylistsSelected": "All playlists selected", + "@selectionAllPlaylistsSelected": { + "description": "Shown when all playlists are selected in selection mode" + }, + "selectionTapPlaylistsToSelect": "Tap playlists to select", + "@selectionTapPlaylistsToSelect": { + "description": "Hint shown in playlist selection mode" + }, + "selectionSelectPlaylistsToDelete": "Select playlists to delete", + "@selectionSelectPlaylistsToDelete": { + "description": "Hint shown when no playlists are selected for deletion" + }, + "audioAnalysisTitle": "Audio Quality Analysis", + "@audioAnalysisTitle": { + "description": "Title for audio analysis section" + }, + "audioAnalysisDescription": "Verify lossless quality with spectrum analysis", + "@audioAnalysisDescription": { + "description": "Description for audio analysis tap-to-analyze prompt" + }, + "audioAnalysisAnalyzing": "Analyzing audio...", + "@audioAnalysisAnalyzing": { + "description": "Loading text while analyzing audio" + }, + "audioAnalysisSampleRate": "Sample Rate", + "@audioAnalysisSampleRate": { + "description": "Sample rate metric label" + }, + "audioAnalysisBitDepth": "Bit Depth", + "@audioAnalysisBitDepth": { + "description": "Bit depth metric label" + }, + "audioAnalysisChannels": "Channels", + "@audioAnalysisChannels": { + "description": "Channels metric label" + }, + "audioAnalysisDuration": "Duration", + "@audioAnalysisDuration": { + "description": "Duration metric label" + }, + "audioAnalysisNyquist": "Nyquist", + "@audioAnalysisNyquist": { + "description": "Nyquist frequency metric label" + }, + "audioAnalysisFileSize": "Size", + "@audioAnalysisFileSize": { + "description": "File size metric label" + }, + "audioAnalysisDynamicRange": "Dynamic Range", + "@audioAnalysisDynamicRange": { + "description": "Dynamic range metric label" + }, + "audioAnalysisPeak": "Peak", + "@audioAnalysisPeak": { + "description": "Peak amplitude metric label" + }, + "audioAnalysisRms": "RMS", + "@audioAnalysisRms": { + "description": "RMS level metric label" + }, + "audioAnalysisSamples": "Samples", + "@audioAnalysisSamples": { + "description": "Total samples metric label" + }, + "extensionsSearchWith": "Search with {providerName}", + "@extensionsSearchWith": { + "description": "Extensions page - subtitle for built-in search provider option", + "placeholders": { + "providerName": { + "type": "String" + } + } + }, + "extensionsHomeFeedProvider": "Home Feed Provider", + "@extensionsHomeFeedProvider": { + "description": "Extensions page - label for home feed provider selector" + }, + "extensionsHomeFeedDescription": "Choose which extension provides the home feed on the main screen", + "@extensionsHomeFeedDescription": { + "description": "Extensions page - description for home feed provider picker" + }, + "extensionsHomeFeedAuto": "Auto", + "@extensionsHomeFeedAuto": { + "description": "Label for auto-selected search provider" + }, + "extensionsHomeFeedAutoSubtitle": "Automatically select the best available", + "@extensionsHomeFeedAutoSubtitle": { + "description": "Extensions page - subtitle for auto home feed option" + }, + "extensionsHomeFeedUse": "Use {extensionName} home feed", + "@extensionsHomeFeedUse": { + "description": "Extensions page - subtitle for a specific extension home feed option", + "placeholders": { + "extensionName": { + "type": "String" + } + } + }, + "extensionsNoHomeFeedExtensions": "No extensions with home feed", + "@extensionsNoHomeFeedExtensions": { + "description": "Extensions page - shown when no installed extension has home feed" + }, + "sortAlphaAsc": "A-Z", + "@sortAlphaAsc": { + "description": "Sort option - alphabetical ascending" + }, + "sortAlphaDesc": "Z-A", + "@sortAlphaDesc": { + "description": "Sort option - alphabetical descending" + }, + "cancelDownloadTitle": "Cancel download?", + "@cancelDownloadTitle": { + "description": "Dialog title when confirming cancellation of an active download" + }, + "cancelDownloadContent": "This will cancel the active download for \"{trackName}\".", + "@cancelDownloadContent": { + "description": "Dialog body when confirming cancellation of an active download", + "placeholders": { + "trackName": { + "type": "String" + } + } + }, + "cancelDownloadKeep": "Keep", + "@cancelDownloadKeep": { + "description": "Dialog button - keep the active download (do not cancel)" + }, + "metadataSaveFailedFfmpeg": "Failed to save metadata via FFmpeg", + "@metadataSaveFailedFfmpeg": { + "description": "Snackbar error when FFmpeg fails to write metadata" + }, + "metadataSaveFailedStorage": "Failed to write metadata back to storage", + "@metadataSaveFailedStorage": { + "description": "Snackbar error when writing metadata file back to storage fails" + }, + "snackbarFolderPickerFailed": "Failed to open folder picker: {error}", + "@snackbarFolderPickerFailed": { + "description": "Snackbar shown when folder picker fails to open", + "placeholders": { + "error": { + "type": "String" + } + } + }, + "errorLoadAlbum": "Failed to load album", + "@errorLoadAlbum": { + "description": "Error state shown when album fails to load" + }, + "errorLoadPlaylist": "Failed to load playlist", + "@errorLoadPlaylist": { + "description": "Error state shown when playlist fails to load" + }, + "errorLoadArtist": "Failed to load artist", + "@errorLoadArtist": { + "description": "Error state shown when artist fails to load" + }, + "notifChannelDownloadName": "Download Progress", + "@notifChannelDownloadName": { + "description": "Android notification channel name for download progress" + }, + "notifChannelDownloadDesc": "Shows download progress for tracks", + "@notifChannelDownloadDesc": { + "description": "Android notification channel description for download progress" + }, + "notifChannelLibraryScanName": "Library Scan", + "@notifChannelLibraryScanName": { + "description": "Android notification channel name for library scan" + }, + "notifChannelLibraryScanDesc": "Shows local library scan progress", + "@notifChannelLibraryScanDesc": { + "description": "Android notification channel description for library scan" + }, + "notifDownloadingTrack": "Downloading {trackName}", + "@notifDownloadingTrack": { + "description": "Notification title while downloading a track", + "placeholders": { + "trackName": { + "type": "String" + } + } + }, + "notifFinalizingTrack": "Finalizing {trackName}", + "@notifFinalizingTrack": { + "description": "Notification title while finalizing (embedding metadata) a track", + "placeholders": { + "trackName": { + "type": "String" + } + } + }, + "notifEmbeddingMetadata": "Embedding metadata...", + "@notifEmbeddingMetadata": { + "description": "Notification body while embedding metadata into a downloaded track" + }, + "notifAlreadyInLibraryCount": "Already in Library ({completed}/{total})", + "@notifAlreadyInLibraryCount": { + "description": "Notification title when track is already in library, with count", + "placeholders": { + "completed": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "notifAlreadyInLibrary": "Already in Library", + "@notifAlreadyInLibrary": { + "description": "Notification title when track is already in library" + }, + "notifDownloadCompleteCount": "Download Complete ({completed}/{total})", + "@notifDownloadCompleteCount": { + "description": "Notification title when download is complete, with count", + "placeholders": { + "completed": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "notifDownloadComplete": "Download Complete", + "@notifDownloadComplete": { + "description": "Notification title when a single download is complete" + }, + "notifDownloadsFinished": "Downloads Finished ({completed} done, {failed} failed)", + "@notifDownloadsFinished": { + "description": "Notification title when queue finishes with some failures", + "placeholders": { + "completed": { + "type": "int" + }, + "failed": { + "type": "int" + } + } + }, + "notifAllDownloadsComplete": "All Downloads Complete", + "@notifAllDownloadsComplete": { + "description": "Notification title when all downloads finish successfully" + }, + "notifTracksDownloadedSuccess": "{count} tracks downloaded successfully", + "@notifTracksDownloadedSuccess": { + "description": "Notification body for queue complete - how many tracks were downloaded", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "notifScanningLibrary": "Scanning local library", + "@notifScanningLibrary": { + "description": "Notification title while scanning local library" + }, + "notifLibraryScanProgressWithTotal": "{scanned}/{total} files • {percentage}%", + "@notifLibraryScanProgressWithTotal": { + "description": "Notification body for library scan progress when total is known", + "placeholders": { + "scanned": { + "type": "int" + }, + "total": { + "type": "int" + }, + "percentage": { + "type": "int" + } + } + }, + "notifLibraryScanProgressNoTotal": "{scanned} files scanned • {percentage}%", + "@notifLibraryScanProgressNoTotal": { + "description": "Notification body for library scan progress when total is unknown", + "placeholders": { + "scanned": { + "type": "int" + }, + "percentage": { + "type": "int" + } + } + }, + "notifLibraryScanComplete": "Library scan complete", + "@notifLibraryScanComplete": { + "description": "Notification title when library scan finishes" + }, + "notifLibraryScanCompleteBody": "{count} tracks indexed", + "@notifLibraryScanCompleteBody": { + "description": "Notification body for library scan complete - number of indexed tracks", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "notifLibraryScanExcluded": "{count} excluded", + "@notifLibraryScanExcluded": { + "description": "Library scan complete suffix - excluded track count", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "notifLibraryScanErrors": "{count} errors", + "@notifLibraryScanErrors": { + "description": "Library scan complete suffix - error count", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "notifLibraryScanFailed": "Library scan failed", + "@notifLibraryScanFailed": { + "description": "Notification title when library scan fails" + }, + "notifLibraryScanCancelled": "Library scan cancelled", + "@notifLibraryScanCancelled": { + "description": "Notification title when library scan is cancelled by the user" + }, + "notifLibraryScanStopped": "Scan stopped before completion.", + "@notifLibraryScanStopped": { + "description": "Notification body when library scan is cancelled" + }, + "notifDownloadingUpdate": "Downloading SpotiFLAC v{version}", + "@notifDownloadingUpdate": { + "description": "Notification title while downloading an app update", + "placeholders": { + "version": { + "type": "String" + } + } + }, + "notifUpdateProgress": "{received} / {total} MB • {percentage}%", + "@notifUpdateProgress": { + "description": "Notification body showing update download progress", + "placeholders": { + "received": { + "type": "String" + }, + "total": { + "type": "String" + }, + "percentage": { + "type": "int" + } + } + }, + "notifUpdateReady": "Update Ready", + "@notifUpdateReady": { + "description": "Notification title when app update download is complete" + }, + "notifUpdateReadyBody": "SpotiFLAC v{version} downloaded. Tap to install.", + "@notifUpdateReadyBody": { + "description": "Notification body when app update is ready to install", + "placeholders": { + "version": { + "type": "String" + } + } + }, + "notifUpdateFailed": "Update Failed", + "@notifUpdateFailed": { + "description": "Notification title when app update download fails" + }, + "notifUpdateFailedBody": "Could not download update. Try again later.", + "@notifUpdateFailedBody": { + "description": "Notification body when app update download fails" + }, + "settingsFiles": "Files & Folders", + "@settingsFiles": { + "description": "Settings menu item - file and folder settings" + }, + "settingsFilesSubtitle": "Download location, filename, folder structure", + "@settingsFilesSubtitle": { + "description": "Subtitle for files & folders settings" + }, + "settingsMetadata": "Metadata", + "@settingsMetadata": { + "description": "Settings menu item - metadata settings" + }, + "settingsMetadataSubtitle": "Cover art, tags, ReplayGain, providers", + "@settingsMetadataSubtitle": { + "description": "Subtitle for metadata settings" + }, + "settingsLyrics": "Lyrics", + "@settingsLyrics": { + "description": "Settings menu item - lyrics settings" + }, + "settingsLyricsSubtitle": "Embed, mode, providers, language options", + "@settingsLyricsSubtitle": { + "description": "Subtitle for lyrics settings" + }, + "settingsApp": "App", + "@settingsApp": { + "description": "Settings menu item - app settings" + }, + "settingsAppSubtitle": "Updates, data, extension repo, debug", + "@settingsAppSubtitle": { + "description": "Subtitle for app settings" + }, + "sectionMetadataProviders": "Providers", + "@sectionMetadataProviders": { + "description": "Settings section header for metadata providers" + }, + "sectionDuplicates": "Duplicates", + "@sectionDuplicates": { + "description": "Settings section header for deduplication" + }, + "sectionLyricsProviderOptions": "Provider Options", + "@sectionLyricsProviderOptions": { + "description": "Settings section header for per-provider lyrics options" + }, + "metadataProvidersTitle": "Metadata Provider Priority", + "@metadataProvidersTitle": { + "description": "Settings item title for metadata provider order" + }, + "metadataProvidersSubtitle": "Drag to set search and metadata source order", + "@metadataProvidersSubtitle": { + "description": "Subtitle for metadata provider priority item" + }, + "downloadDeduplication": "Skip Duplicate Downloads", + "@downloadDeduplication": { + "description": "Setting - skip tracks already in download history" + }, + "downloadDeduplicationEnabled": "Already-downloaded tracks will be skipped", + "@downloadDeduplicationEnabled": { + "description": "Subtitle when deduplication is on" + }, + "downloadDeduplicationDisabled": "All tracks will be downloaded regardless of history", + "@downloadDeduplicationDisabled": { + "description": "Subtitle when deduplication is off" + }, + "downloadFallbackExtensions": "Fallback Extensions", + "@downloadFallbackExtensions": { + "description": "Settings item for configuring fallback extension providers" + }, + "downloadFallbackExtensionsSubtitle": "Choose which extensions can be used as fallback", + "@downloadFallbackExtensionsSubtitle": { + "description": "Subtitle for fallback extensions item" } } diff --git a/lib/l10n/arb/app_zh_CN.arb b/lib/l10n/arb/app_zh_CN.arb index 5b271364..cd341cef 100644 --- a/lib/l10n/arb/app_zh_CN.arb +++ b/lib/l10n/arb/app_zh_CN.arb @@ -4509,5 +4509,77 @@ "notifUpdateFailedBody": "Could not download update. Try again later.", "@notifUpdateFailedBody": { "description": "Notification body when app update download fails" + }, + "settingsFiles": "Files & Folders", + "@settingsFiles": { + "description": "Settings menu item - file and folder settings" + }, + "settingsFilesSubtitle": "Download location, filename, folder structure", + "@settingsFilesSubtitle": { + "description": "Subtitle for files & folders settings" + }, + "settingsMetadata": "Metadata", + "@settingsMetadata": { + "description": "Settings menu item - metadata settings" + }, + "settingsMetadataSubtitle": "Cover art, tags, ReplayGain, providers", + "@settingsMetadataSubtitle": { + "description": "Subtitle for metadata settings" + }, + "settingsLyrics": "Lyrics", + "@settingsLyrics": { + "description": "Settings menu item - lyrics settings" + }, + "settingsLyricsSubtitle": "Embed, mode, providers, language options", + "@settingsLyricsSubtitle": { + "description": "Subtitle for lyrics settings" + }, + "settingsApp": "App", + "@settingsApp": { + "description": "Settings menu item - app settings" + }, + "settingsAppSubtitle": "Updates, data, extension repo, debug", + "@settingsAppSubtitle": { + "description": "Subtitle for app settings" + }, + "sectionMetadataProviders": "Providers", + "@sectionMetadataProviders": { + "description": "Settings section header for metadata providers" + }, + "sectionDuplicates": "Duplicates", + "@sectionDuplicates": { + "description": "Settings section header for deduplication" + }, + "sectionLyricsProviderOptions": "Provider Options", + "@sectionLyricsProviderOptions": { + "description": "Settings section header for per-provider lyrics options" + }, + "metadataProvidersTitle": "Metadata Provider Priority", + "@metadataProvidersTitle": { + "description": "Settings item title for metadata provider order" + }, + "metadataProvidersSubtitle": "Drag to set search and metadata source order", + "@metadataProvidersSubtitle": { + "description": "Subtitle for metadata provider priority item" + }, + "downloadDeduplication": "Skip Duplicate Downloads", + "@downloadDeduplication": { + "description": "Setting - skip tracks already in download history" + }, + "downloadDeduplicationEnabled": "Already-downloaded tracks will be skipped", + "@downloadDeduplicationEnabled": { + "description": "Subtitle when deduplication is on" + }, + "downloadDeduplicationDisabled": "All tracks will be downloaded regardless of history", + "@downloadDeduplicationDisabled": { + "description": "Subtitle when deduplication is off" + }, + "downloadFallbackExtensions": "Fallback Extensions", + "@downloadFallbackExtensions": { + "description": "Settings item for configuring fallback extension providers" + }, + "downloadFallbackExtensionsSubtitle": "Choose which extensions can be used as fallback", + "@downloadFallbackExtensionsSubtitle": { + "description": "Subtitle for fallback extensions item" } } diff --git a/lib/l10n/arb/app_zh_TW.arb b/lib/l10n/arb/app_zh_TW.arb index 4d9a042a..139262bf 100644 --- a/lib/l10n/arb/app_zh_TW.arb +++ b/lib/l10n/arb/app_zh_TW.arb @@ -4509,5 +4509,77 @@ "notifUpdateFailedBody": "Could not download update. Try again later.", "@notifUpdateFailedBody": { "description": "Notification body when app update download fails" + }, + "settingsFiles": "Files & Folders", + "@settingsFiles": { + "description": "Settings menu item - file and folder settings" + }, + "settingsFilesSubtitle": "Download location, filename, folder structure", + "@settingsFilesSubtitle": { + "description": "Subtitle for files & folders settings" + }, + "settingsMetadata": "Metadata", + "@settingsMetadata": { + "description": "Settings menu item - metadata settings" + }, + "settingsMetadataSubtitle": "Cover art, tags, ReplayGain, providers", + "@settingsMetadataSubtitle": { + "description": "Subtitle for metadata settings" + }, + "settingsLyrics": "Lyrics", + "@settingsLyrics": { + "description": "Settings menu item - lyrics settings" + }, + "settingsLyricsSubtitle": "Embed, mode, providers, language options", + "@settingsLyricsSubtitle": { + "description": "Subtitle for lyrics settings" + }, + "settingsApp": "App", + "@settingsApp": { + "description": "Settings menu item - app settings" + }, + "settingsAppSubtitle": "Updates, data, extension repo, debug", + "@settingsAppSubtitle": { + "description": "Subtitle for app settings" + }, + "sectionMetadataProviders": "Providers", + "@sectionMetadataProviders": { + "description": "Settings section header for metadata providers" + }, + "sectionDuplicates": "Duplicates", + "@sectionDuplicates": { + "description": "Settings section header for deduplication" + }, + "sectionLyricsProviderOptions": "Provider Options", + "@sectionLyricsProviderOptions": { + "description": "Settings section header for per-provider lyrics options" + }, + "metadataProvidersTitle": "Metadata Provider Priority", + "@metadataProvidersTitle": { + "description": "Settings item title for metadata provider order" + }, + "metadataProvidersSubtitle": "Drag to set search and metadata source order", + "@metadataProvidersSubtitle": { + "description": "Subtitle for metadata provider priority item" + }, + "downloadDeduplication": "Skip Duplicate Downloads", + "@downloadDeduplication": { + "description": "Setting - skip tracks already in download history" + }, + "downloadDeduplicationEnabled": "Already-downloaded tracks will be skipped", + "@downloadDeduplicationEnabled": { + "description": "Subtitle when deduplication is on" + }, + "downloadDeduplicationDisabled": "All tracks will be downloaded regardless of history", + "@downloadDeduplicationDisabled": { + "description": "Subtitle when deduplication is off" + }, + "downloadFallbackExtensions": "Fallback Extensions", + "@downloadFallbackExtensions": { + "description": "Settings item for configuring fallback extension providers" + }, + "downloadFallbackExtensionsSubtitle": "Choose which extensions can be used as fallback", + "@downloadFallbackExtensionsSubtitle": { + "description": "Subtitle for fallback extensions item" } }