forked from akai/readest
feat(applock): 4-digit PIN gate at app launch (#4093)
Closes #2285. Adds an opt-in 4-digit PIN that gates the library and reader on app launch. Threat model: casual physical/browser access by another person on a shared device — peace of mind, not defense against an attacker with filesystem access. The PIN is stored as a salted PBKDF2-SHA256 hash (100k iterations) in settings.json; the plaintext PIN is never persisted. Configured from Settings → Advanced Settings → "Set PIN…" (and "Change PIN…" / "Disable PIN…" once enabled). The lock screen and the set/change/disable dialog share a single 4-dot input component (PinInput) for a consistent UI; the dialog auto-advances focus from Current → New → Confirm. Lock-on-resume, biometric unlock, and account-based reset are out of scope for this MVP — disable for now is "clear app data". Bundles the previously-missed sync-passphrase i18n strings (PR #4090) across all 33 locales so no `__STRING_NOT_TRANSLATED__` placeholders remain in the tree. New - src/libs/crypto/applock.ts (PBKDF2 hash/verify; reuses derivePbkdf2Key) - src/store/appLockStore.ts (gate + dialog state) - src/components/PinInput.tsx (shared 4-dot input) - src/components/AppLockScreen.tsx (full-screen lock gate) - src/components/settings/AppLockDialog.tsx (set/change/disable) - src/__tests__/libs/crypto/applock.test.ts Modified - src/types/settings.ts (pinCodeEnabled / pinCodeHash / pinCodeSalt) - src/services/constants.ts (default off) - src/components/Providers.tsx (mount gate + dialog above app shell) - src/app/library/components/SettingsMenu.tsx (Advanced submenu entries) - src/styles/globals.css (animate-pin-shake keyframe) - public/locales/*/translation.json (21 PIN keys + 17 leftover passphrase keys × 33 locales) Verified - pnpm test (4018 pass) - pnpm lint (clean) - Manual web smoke: Set/Reload-locks/Wrong-PIN/Unlock/Change/Disable Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1436,5 +1436,43 @@
|
||||
"Failed to download file: {{title}}": "فشل تنزيل الملف: {{title}}",
|
||||
"Failed to delete cloud copy of the file: {{title}}": "فشل في حذف النسخة السحابية للملف: {{title}}",
|
||||
"{{percentage}}% used": "تم استخدام {{percentage}}٪",
|
||||
"Resets in {{duration}}": "يُعاد التعيين خلال {{duration}}"
|
||||
"Resets in {{duration}}": "يُعاد التعيين خلال {{duration}}",
|
||||
"Set PIN…": "تعيين رمز PIN…",
|
||||
"Require a 4-digit PIN to open Readest": "اطلب رمز PIN مكوّن من 4 أرقام لفتح Readest",
|
||||
"Change PIN…": "تغيير رمز PIN…",
|
||||
"Disable PIN…": "تعطيل رمز PIN…",
|
||||
"Sync passphrase ready": "عبارة مرور المزامنة جاهزة",
|
||||
"This permanently deletes the encrypted credentials we sync (e.g., OPDS catalog passwords) on every device. Local copies are preserved. You will need to re-enter the sync passphrase or set a new one. Continue?": "سيؤدي هذا إلى حذف بيانات الاعتماد المشفرة التي نقوم بمزامنتها (مثل كلمات مرور كتالوج OPDS) على كل جهاز بشكل دائم. سيتم الاحتفاظ بالنسخ المحلية. ستحتاج إلى إعادة إدخال عبارة مرور المزامنة أو تعيين عبارة جديدة. هل تريد المتابعة؟",
|
||||
"Sync passphrase forgotten — all encrypted fields cleared": "تم نسيان عبارة مرور المزامنة — تم مسح جميع الحقول المشفرة",
|
||||
"Sync passphrase": "عبارة مرور المزامنة",
|
||||
"Encrypts sensitive synced fields (like OPDS catalog credentials) before they leave your device. Set one now or wait — it will be requested when needed.": "يقوم بتشفير الحقول الحساسة المتزامنة (مثل بيانات اعتماد كتالوج OPDS) قبل مغادرتها لجهازك. قم بتعيين عبارة الآن أو انتظر — سيُطلب منك ذلك عند الحاجة.",
|
||||
"Set on this account. Will be requested when needed to decrypt synced credentials.": "تم تعيينها في هذا الحساب. سيُطلب منك إدخالها عند الحاجة لفك تشفير بيانات الاعتماد المتزامنة.",
|
||||
"Set passphrase": "تعيين عبارة المرور",
|
||||
"Unlock now": "إلغاء القفل الآن",
|
||||
"Forgot passphrase": "نسيت عبارة المرور",
|
||||
"Incorrect PIN": "رمز PIN غير صحيح",
|
||||
"App locked": "التطبيق مقفل",
|
||||
"Enter your PIN": "أدخل رمز PIN",
|
||||
"Readest is locked. Enter your 4-digit PIN to continue.": "Readest مقفل. أدخل رمز PIN المكوّن من 4 أرقام للمتابعة.",
|
||||
"PIN code": "رمز PIN",
|
||||
"Forgetting your PIN locks you out of this device. You'll need to clear the app's data to reset it.": "نسيان رمز PIN يمنعك من الوصول إلى هذا الجهاز. ستحتاج إلى مسح بيانات التطبيق لإعادة تعيينه.",
|
||||
"Passphrase must be at least 8 characters": "يجب أن تتكون عبارة المرور من 8 أحرف على الأقل",
|
||||
"Passphrases do not match": "عبارات المرور غير متطابقة",
|
||||
"Set sync passphrase": "تعيين عبارة مرور المزامنة",
|
||||
"Enter sync passphrase": "أدخل عبارة مرور المزامنة",
|
||||
"A sync passphrase encrypts your sensitive fields (like OPDS catalog credentials) before they sync. We never see this passphrase. Pick something memorable — there is no recovery without it.": "تقوم عبارة مرور المزامنة بتشفير الحقول الحساسة (مثل بيانات اعتماد كتالوج OPDS) قبل مزامنتها. نحن لا نرى عبارة المرور هذه أبدًا. اختر شيئًا يمكن تذكره — لا توجد طريقة لاستعادتها بدونها.",
|
||||
"Enter the sync passphrase you set on another device to decrypt your synced credentials.": "أدخل عبارة مرور المزامنة التي عيّنتها على جهاز آخر لفك تشفير بيانات الاعتماد المتزامنة.",
|
||||
"Confirm passphrase": "تأكيد عبارة المرور",
|
||||
"Unlock": "إلغاء القفل",
|
||||
"Set PIN": "تعيين رمز PIN",
|
||||
"Change PIN": "تغيير رمز PIN",
|
||||
"Disable PIN": "تعطيل رمز PIN",
|
||||
"Pick a 4-digit PIN. You will need to enter it every time you open Readest. There is no way to recover a forgotten PIN — clearing the app data is the only way to reset it.": "اختر رمز PIN مكوّن من 4 أرقام. ستحتاج إلى إدخاله في كل مرة تفتح فيها Readest. لا توجد طريقة لاستعادة رمز PIN منسي — مسح بيانات التطبيق هو الطريقة الوحيدة لإعادة تعيينه.",
|
||||
"Enter your current PIN, then choose a new 4-digit PIN.": "أدخل رمز PIN الحالي، ثم اختر رمز PIN جديد مكوّن من 4 أرقام.",
|
||||
"Enter your current PIN to disable the app lock.": "أدخل رمز PIN الحالي لتعطيل قفل التطبيق.",
|
||||
"PIN must be {{length}} digits": "يجب أن يتكون رمز PIN من {{length}} أرقام",
|
||||
"PINs do not match": "رمزا PIN غير متطابقين",
|
||||
"Current PIN": "رمز PIN الحالي",
|
||||
"New PIN": "رمز PIN جديد",
|
||||
"Confirm new PIN": "تأكيد رمز PIN الجديد"
|
||||
}
|
||||
|
||||
@@ -1340,5 +1340,43 @@
|
||||
"Failed to download file: {{title}}": "ফাইল ডাউনলোড ব্যর্থ: {{title}}",
|
||||
"Failed to delete cloud copy of the file: {{title}}": "ফাইলের ক্লাউড অনুলিপি মুছতে ব্যর্থ: {{title}}",
|
||||
"{{percentage}}% used": "{{percentage}}% ব্যবহৃত",
|
||||
"Resets in {{duration}}": "{{duration}} এ রিসেট হবে"
|
||||
"Resets in {{duration}}": "{{duration}} এ রিসেট হবে",
|
||||
"Set PIN…": "PIN সেট করুন…",
|
||||
"Require a 4-digit PIN to open Readest": "Readest খুলতে একটি ৪-সংখ্যার PIN প্রয়োজন করুন",
|
||||
"Change PIN…": "PIN পরিবর্তন করুন…",
|
||||
"Disable PIN…": "PIN নিষ্ক্রিয় করুন…",
|
||||
"Sync passphrase ready": "সিঙ্ক পাসফ্রেজ প্রস্তুত",
|
||||
"This permanently deletes the encrypted credentials we sync (e.g., OPDS catalog passwords) on every device. Local copies are preserved. You will need to re-enter the sync passphrase or set a new one. Continue?": "এটি প্রতিটি ডিভাইসে আমরা সিঙ্ক করি এমন এনক্রিপ্ট করা শংসাপত্র (যেমন, OPDS ক্যাটালগ পাসওয়ার্ড) স্থায়ীভাবে মুছে ফেলে। স্থানীয় কপিগুলি সংরক্ষিত থাকে। আপনাকে সিঙ্ক পাসফ্রেজ পুনরায় প্রবেশ করতে হবে বা একটি নতুন সেট করতে হবে। চালিয়ে যাবেন?",
|
||||
"Sync passphrase forgotten — all encrypted fields cleared": "সিঙ্ক পাসফ্রেজ ভুলে গিয়েছেন — সমস্ত এনক্রিপ্ট করা ক্ষেত্র সাফ করা হয়েছে",
|
||||
"Sync passphrase": "সিঙ্ক পাসফ্রেজ",
|
||||
"Encrypts sensitive synced fields (like OPDS catalog credentials) before they leave your device. Set one now or wait — it will be requested when needed.": "সংবেদনশীল সিঙ্ক করা ক্ষেত্রগুলি (যেমন OPDS ক্যাটালগ শংসাপত্র) আপনার ডিভাইস ছেড়ে যাওয়ার আগে এনক্রিপ্ট করে। এখন একটি সেট করুন বা অপেক্ষা করুন — প্রয়োজন হলে এটি অনুরোধ করা হবে।",
|
||||
"Set on this account. Will be requested when needed to decrypt synced credentials.": "এই অ্যাকাউন্টে সেট করা হয়েছে। সিঙ্ক করা শংসাপত্রগুলি ডিক্রিপ্ট করার জন্য প্রয়োজন হলে এটি অনুরোধ করা হবে।",
|
||||
"Set passphrase": "পাসফ্রেজ সেট করুন",
|
||||
"Unlock now": "এখনই আনলক করুন",
|
||||
"Forgot passphrase": "পাসফ্রেজ ভুলে গেছেন",
|
||||
"Incorrect PIN": "ভুল PIN",
|
||||
"App locked": "অ্যাপ লক করা হয়েছে",
|
||||
"Enter your PIN": "আপনার PIN লিখুন",
|
||||
"Readest is locked. Enter your 4-digit PIN to continue.": "Readest লক করা আছে। চালিয়ে যেতে আপনার ৪-সংখ্যার PIN লিখুন।",
|
||||
"PIN code": "PIN কোড",
|
||||
"Forgetting your PIN locks you out of this device. You'll need to clear the app's data to reset it.": "PIN ভুলে গেলে আপনি এই ডিভাইসে প্রবেশ করতে পারবেন না। এটি রিসেট করতে অ্যাপের ডেটা মুছে ফেলতে হবে।",
|
||||
"Passphrase must be at least 8 characters": "পাসফ্রেজ কমপক্ষে 8 অক্ষরের হতে হবে",
|
||||
"Passphrases do not match": "পাসফ্রেজগুলি মিলছে না",
|
||||
"Set sync passphrase": "সিঙ্ক পাসফ্রেজ সেট করুন",
|
||||
"Enter sync passphrase": "সিঙ্ক পাসফ্রেজ লিখুন",
|
||||
"A sync passphrase encrypts your sensitive fields (like OPDS catalog credentials) before they sync. We never see this passphrase. Pick something memorable — there is no recovery without it.": "একটি সিঙ্ক পাসফ্রেজ আপনার সংবেদনশীল ক্ষেত্রগুলি (যেমন OPDS ক্যাটালগ শংসাপত্র) সিঙ্ক করার আগে এনক্রিপ্ট করে। আমরা কখনই এই পাসফ্রেজ দেখি না। এমন কিছু বেছে নিন যা মনে রাখা যায় — এটি ছাড়া পুনরুদ্ধার সম্ভব নয়।",
|
||||
"Enter the sync passphrase you set on another device to decrypt your synced credentials.": "আপনার সিঙ্ক করা শংসাপত্রগুলি ডিক্রিপ্ট করতে অন্য একটি ডিভাইসে আপনি যে সিঙ্ক পাসফ্রেজ সেট করেছেন তা লিখুন।",
|
||||
"Confirm passphrase": "পাসফ্রেজ নিশ্চিত করুন",
|
||||
"Unlock": "আনলক করুন",
|
||||
"Set PIN": "PIN সেট করুন",
|
||||
"Change PIN": "PIN পরিবর্তন করুন",
|
||||
"Disable PIN": "PIN নিষ্ক্রিয় করুন",
|
||||
"Pick a 4-digit PIN. You will need to enter it every time you open Readest. There is no way to recover a forgotten PIN — clearing the app data is the only way to reset it.": "একটি ৪-সংখ্যার PIN বেছে নিন। প্রতিবার Readest খোলার সময় আপনাকে এটি লিখতে হবে। ভুলে যাওয়া PIN পুনরুদ্ধারের কোনো উপায় নেই — অ্যাপ ডেটা মুছে ফেলাই এটিকে রিসেট করার একমাত্র উপায়।",
|
||||
"Enter your current PIN, then choose a new 4-digit PIN.": "আপনার বর্তমান PIN লিখুন, তারপর একটি নতুন ৪-সংখ্যার PIN বেছে নিন।",
|
||||
"Enter your current PIN to disable the app lock.": "অ্যাপ লক নিষ্ক্রিয় করতে আপনার বর্তমান PIN লিখুন।",
|
||||
"PIN must be {{length}} digits": "PIN অবশ্যই {{length}} সংখ্যার হতে হবে",
|
||||
"PINs do not match": "PIN মিলছে না",
|
||||
"Current PIN": "বর্তমান PIN",
|
||||
"New PIN": "নতুন PIN",
|
||||
"Confirm new PIN": "নতুন PIN নিশ্চিত করুন"
|
||||
}
|
||||
|
||||
@@ -1316,5 +1316,43 @@
|
||||
"Failed to download file: {{title}}": "ཡིག་ཆ་ཕབ་ལེན་བྱས་མ་ཐུབ། {{title}}",
|
||||
"Failed to delete cloud copy of the file: {{title}}": "ཡིག་ཆའི་སྤྲི་དོན་གྱི་འདྲ་བཤུས་བསུབ་ཐུབ་མེད། {{title}}",
|
||||
"{{percentage}}% used": "སྤྱོད་ཟིན་ {{percentage}}%",
|
||||
"Resets in {{duration}}": "{{duration}} ནང་སླར་གསོ་བྱེད།"
|
||||
"Resets in {{duration}}": "{{duration}} ནང་སླར་གསོ་བྱེད།",
|
||||
"Set PIN…": "PIN སྒྲིག་པ་…",
|
||||
"Require a 4-digit PIN to open Readest": "Readest ཁ་ཕྱེ་བར་ཨང་གྲངས་ ༤ ཡོད་པའི་ PIN ཞིག་དགོས",
|
||||
"Change PIN…": "PIN བརྗེ་བ་…",
|
||||
"Disable PIN…": "PIN སྤོ་བ་…",
|
||||
"Sync passphrase ready": "མཉམ་སྦྱོར་གསང་ཚིག་གྲ་སྒྲིག་ཟིན་པ།",
|
||||
"This permanently deletes the encrypted credentials we sync (e.g., OPDS catalog passwords) on every device. Local copies are preserved. You will need to re-enter the sync passphrase or set a new one. Continue?": "འདིས་ཡིག་ཆ་ཁག་གསང་སྦས་སུ་མཉམ་སྦྱོར་བྱས་པ་རྣམས་ (དཔེར་ན། OPDS དཀར་ཆག་གི་གསང་ཨང་) སྒྲིག་ཆས་རེ་རེའི་ཐོག་གཏན་འཇགས་སུ་སུབ་སྲིད། ས་གནས་ཀྱི་པར་གཞི་ཉར་ཡོད། ཁྱོད་ཀྱིས་མཉམ་སྦྱོར་གསང་ཚིག་སྔར་བཞིན་འཇུག་པའམ་གསར་པ་སྒྲིག་དགོས། མུ་མཐུད་དམ།",
|
||||
"Sync passphrase forgotten — all encrypted fields cleared": "མཉམ་སྦྱོར་གསང་ཚིག་བརྗེད་སོང་ — གསང་སྦས་ཀྱི་ས་སྟོང་ཚང་མ་སུབ་ཟིན།",
|
||||
"Sync passphrase": "མཉམ་སྦྱོར་གསང་ཚིག",
|
||||
"Encrypts sensitive synced fields (like OPDS catalog credentials) before they leave your device. Set one now or wait — it will be requested when needed.": "ཁྱོད་ཀྱི་སྒྲིག་ཆས་ལས་མ་འགྲོ་གོང་གལ་ཆེའི་མཉམ་སྦྱོར་ས་སྟོང་རྣམས་ (དཔེར་ན་ OPDS དཀར་ཆག་གི་ངོས་འཛིན་ཆ་འཕྲིན) གསང་སྦས་སུ་སྒྱུར། ད་ལྟ་གཅིག་སྒྲིག་པའམ་སྒུག་ཡོད། — དགོས་མཁོ་འབྱུང་སྐབས་ཞུ་འདུག",
|
||||
"Set on this account. Will be requested when needed to decrypt synced credentials.": "ཁྱེད་ཀྱི་རྩིས་ཐོ་འདིར་སྒྲིག་ཟིན། མཉམ་སྦྱོར་གྱི་ངོས་འཛིན་ཆ་འཕྲིན་གསང་སྦས་འགྲོལ་སྐབས་དགོས་ཚེ་ཞུ་འདུག",
|
||||
"Set passphrase": "གསང་ཚིག་སྒྲིག་པ།",
|
||||
"Unlock now": "ད་ལྟ་ཁ་ཕྱེ།",
|
||||
"Forgot passphrase": "གསང་ཚིག་བརྗེད་སོང་།",
|
||||
"Incorrect PIN": "PIN ནོར་འདུག",
|
||||
"App locked": "མཉེན་ཆས་སྒོ་བརྒྱབ་ཡོད",
|
||||
"Enter your PIN": "ཁྱེད་ཀྱི་ PIN འབྲི་རོགས",
|
||||
"Readest is locked. Enter your 4-digit PIN to continue.": "Readest སྒོ་བརྒྱབ་ཡོད། མུ་མཐུད་པར་ཨང་གྲངས་ ༤ ཡོད་པའི་ PIN འབྲི་རོགས།",
|
||||
"PIN code": "PIN ཨང་གྲངས",
|
||||
"Forgetting your PIN locks you out of this device. You'll need to clear the app's data to reset it.": "PIN བརྗེད་ན་ཁྱེད་ཀྱིས་སྒྲིག་ཆས་འདིར་འཛུལ་མི་ཐུབ། སླར་སྒྲིག་པར་མཉེན་ཆས་ཀྱི་གཞི་གྲངས་གཙང་སེལ་བྱ་དགོས།",
|
||||
"Passphrase must be at least 8 characters": "གསང་ཚིག་ལ་ཡི་གེ་གྲངས་ ༨ ལས་མི་ཉུང་བ་དགོས།",
|
||||
"Passphrases do not match": "གསང་ཚིག་གཉིས་མཐུན་མི་འདུག",
|
||||
"Set sync passphrase": "མཉམ་སྦྱོར་གསང་ཚིག་སྒྲིག་པ།",
|
||||
"Enter sync passphrase": "མཉམ་སྦྱོར་གསང་ཚིག་འཇུག་རོགས།",
|
||||
"A sync passphrase encrypts your sensitive fields (like OPDS catalog credentials) before they sync. We never see this passphrase. Pick something memorable — there is no recovery without it.": "མཉམ་སྦྱོར་གསང་ཚིག་གིས་གལ་ཆེའི་ས་སྟོང་རྣམས་ (དཔེར་ན་ OPDS དཀར་ཆག་གི་ངོས་འཛིན་ཆ་འཕྲིན) མཉམ་སྦྱོར་མ་བྱས་གོང་གསང་སྦས་སུ་སྒྱུར། ང་ཚོས་གསང་ཚིག་འདི་ནམ་ཡང་མཐོང་མི་ཐུབ། དྲན་སླ་བ་ཞིག་འདེམས་རོགས། — དེ་མེད་ན་སླར་འཚོལ་མི་ཐུབ།",
|
||||
"Enter the sync passphrase you set on another device to decrypt your synced credentials.": "ཁྱེད་ཀྱི་མཉམ་སྦྱོར་ངོས་འཛིན་ཆ་འཕྲིན་གསང་སྦས་འགྲོལ་ཆེད་སྒྲིག་ཆས་གཞན་པོར་སྒྲིག་པའི་མཉམ་སྦྱོར་གསང་ཚིག་འཇུག་རོགས།",
|
||||
"Confirm passphrase": "གསང་ཚིག་གཏན་འཁེལ།",
|
||||
"Unlock": "ཁ་ཕྱེ།",
|
||||
"Set PIN": "PIN སྒྲིག་པ",
|
||||
"Change PIN": "PIN བརྗེ་བ",
|
||||
"Disable PIN": "PIN སྤོ་བ",
|
||||
"Pick a 4-digit PIN. You will need to enter it every time you open Readest. There is no way to recover a forgotten PIN — clearing the app data is the only way to reset it.": "ཨང་གྲངས་ ༤ ཡོད་པའི་ PIN ཞིག་འདེམས། ཁྱེད་ཀྱིས་ Readest ཁ་ཕྱེ་སྐབས་རེར་འདི་འབྲི་དགོས། བརྗེད་སོང་བའི་ PIN སླར་འདྲེན་བྱེད་ཐབས་མེད — མཉེན་ཆས་ཀྱི་གཞི་གྲངས་གཙང་སེལ་བྱ་བ་ནི་སླར་སྒྲིག་པའི་ཐབས་ལམ་གཅིག་པོ་དེ་རེད།",
|
||||
"Enter your current PIN, then choose a new 4-digit PIN.": "ཁྱེད་ཀྱི་ད་ལྟའི་ PIN འབྲི་རྗེས། ཨང་གྲངས་ ༤ ཡོད་པའི་ PIN གསར་པ་ཞིག་འདེམས།",
|
||||
"Enter your current PIN to disable the app lock.": "མཉེན་ཆས་ཀྱི་སྒོ་བརྒྱབ་པ་སྤོ་བར་ཁྱེད་ཀྱི་ད་ལྟའི་ PIN འབྲི་རོགས།",
|
||||
"PIN must be {{length}} digits": "PIN ནི་ཨང་གྲངས་ {{length}} ཡིན་དགོས",
|
||||
"PINs do not match": "PIN མི་འདྲ",
|
||||
"Current PIN": "ད་ལྟའི་ PIN",
|
||||
"New PIN": "PIN གསར་པ",
|
||||
"Confirm new PIN": "PIN གསར་པ་གཏན་འཁེལ"
|
||||
}
|
||||
|
||||
@@ -1340,5 +1340,43 @@
|
||||
"Failed to download file: {{title}}": "Fehler beim Herunterladen der Datei: {{title}}",
|
||||
"Failed to delete cloud copy of the file: {{title}}": "Fehler beim Löschen der Cloud-Kopie der Datei: {{title}}",
|
||||
"{{percentage}}% used": "{{percentage}}% verbraucht",
|
||||
"Resets in {{duration}}": "Setzt sich in {{duration}} zurück"
|
||||
"Resets in {{duration}}": "Setzt sich in {{duration}} zurück",
|
||||
"Set PIN…": "PIN festlegen…",
|
||||
"Require a 4-digit PIN to open Readest": "Eine 4-stellige PIN zum Öffnen von Readest verlangen",
|
||||
"Change PIN…": "PIN ändern…",
|
||||
"Disable PIN…": "PIN deaktivieren…",
|
||||
"Sync passphrase ready": "Sync-Passphrase bereit",
|
||||
"This permanently deletes the encrypted credentials we sync (e.g., OPDS catalog passwords) on every device. Local copies are preserved. You will need to re-enter the sync passphrase or set a new one. Continue?": "Dies löscht dauerhaft die verschlüsselten Anmeldedaten, die wir auf jedem Gerät synchronisieren (z. B. OPDS-Katalog-Passwörter). Lokale Kopien bleiben erhalten. Sie müssen die Sync-Passphrase erneut eingeben oder eine neue festlegen. Fortfahren?",
|
||||
"Sync passphrase forgotten — all encrypted fields cleared": "Sync-Passphrase vergessen — alle verschlüsselten Felder wurden gelöscht",
|
||||
"Sync passphrase": "Sync-Passphrase",
|
||||
"Encrypts sensitive synced fields (like OPDS catalog credentials) before they leave your device. Set one now or wait — it will be requested when needed.": "Verschlüsselt sensible synchronisierte Felder (z. B. OPDS-Katalog-Anmeldedaten), bevor sie Ihr Gerät verlassen. Legen Sie jetzt eine fest oder warten Sie — sie wird bei Bedarf angefordert.",
|
||||
"Set on this account. Will be requested when needed to decrypt synced credentials.": "Für dieses Konto festgelegt. Wird bei Bedarf angefordert, um synchronisierte Anmeldedaten zu entschlüsseln.",
|
||||
"Set passphrase": "Passphrase festlegen",
|
||||
"Unlock now": "Jetzt entsperren",
|
||||
"Forgot passphrase": "Passphrase vergessen",
|
||||
"Incorrect PIN": "Falsche PIN",
|
||||
"App locked": "App gesperrt",
|
||||
"Enter your PIN": "PIN eingeben",
|
||||
"Readest is locked. Enter your 4-digit PIN to continue.": "Readest ist gesperrt. Geben Sie Ihre 4-stellige PIN ein, um fortzufahren.",
|
||||
"PIN code": "PIN-Code",
|
||||
"Forgetting your PIN locks you out of this device. You'll need to clear the app's data to reset it.": "Wenn Sie Ihre PIN vergessen, sperren Sie sich von diesem Gerät aus. Sie müssen die App-Daten löschen, um sie zurückzusetzen.",
|
||||
"Passphrase must be at least 8 characters": "Die Passphrase muss mindestens 8 Zeichen lang sein",
|
||||
"Passphrases do not match": "Die Passphrasen stimmen nicht überein",
|
||||
"Set sync passphrase": "Sync-Passphrase festlegen",
|
||||
"Enter sync passphrase": "Sync-Passphrase eingeben",
|
||||
"A sync passphrase encrypts your sensitive fields (like OPDS catalog credentials) before they sync. We never see this passphrase. Pick something memorable — there is no recovery without it.": "Eine Sync-Passphrase verschlüsselt Ihre sensiblen Felder (wie OPDS-Katalog-Anmeldedaten), bevor sie synchronisiert werden. Wir sehen diese Passphrase nie. Wählen Sie etwas Einprägsames — ohne sie ist keine Wiederherstellung möglich.",
|
||||
"Enter the sync passphrase you set on another device to decrypt your synced credentials.": "Geben Sie die Sync-Passphrase ein, die Sie auf einem anderen Gerät festgelegt haben, um Ihre synchronisierten Anmeldedaten zu entschlüsseln.",
|
||||
"Confirm passphrase": "Passphrase bestätigen",
|
||||
"Unlock": "Entsperren",
|
||||
"Set PIN": "PIN festlegen",
|
||||
"Change PIN": "PIN ändern",
|
||||
"Disable PIN": "PIN deaktivieren",
|
||||
"Pick a 4-digit PIN. You will need to enter it every time you open Readest. There is no way to recover a forgotten PIN — clearing the app data is the only way to reset it.": "Wählen Sie eine 4-stellige PIN. Sie müssen sie jedes Mal eingeben, wenn Sie Readest öffnen. Es gibt keine Möglichkeit, eine vergessene PIN wiederherzustellen — das Löschen der App-Daten ist die einzige Möglichkeit, sie zurückzusetzen.",
|
||||
"Enter your current PIN, then choose a new 4-digit PIN.": "Geben Sie Ihre aktuelle PIN ein und wählen Sie dann eine neue 4-stellige PIN.",
|
||||
"Enter your current PIN to disable the app lock.": "Geben Sie Ihre aktuelle PIN ein, um die App-Sperre zu deaktivieren.",
|
||||
"PIN must be {{length}} digits": "Die PIN muss aus {{length}} Ziffern bestehen",
|
||||
"PINs do not match": "PINs stimmen nicht überein",
|
||||
"Current PIN": "Aktuelle PIN",
|
||||
"New PIN": "Neue PIN",
|
||||
"Confirm new PIN": "Neue PIN bestätigen"
|
||||
}
|
||||
|
||||
@@ -1340,5 +1340,43 @@
|
||||
"Failed to download file: {{title}}": "Αποτυχία λήψης αρχείου: {{title}}",
|
||||
"Failed to delete cloud copy of the file: {{title}}": "Αποτυχία διαγραφής του αντιγράφου του αρχείου: {{title}}",
|
||||
"{{percentage}}% used": "{{percentage}}% σε χρήση",
|
||||
"Resets in {{duration}}": "Επαναφορά σε {{duration}}"
|
||||
"Resets in {{duration}}": "Επαναφορά σε {{duration}}",
|
||||
"Set PIN…": "Ορισμός PIN…",
|
||||
"Require a 4-digit PIN to open Readest": "Απαιτείται 4ψήφιο PIN για το άνοιγμα του Readest",
|
||||
"Change PIN…": "Αλλαγή PIN…",
|
||||
"Disable PIN…": "Απενεργοποίηση PIN…",
|
||||
"Sync passphrase ready": "Η φράση πρόσβασης συγχρονισμού είναι έτοιμη",
|
||||
"This permanently deletes the encrypted credentials we sync (e.g., OPDS catalog passwords) on every device. Local copies are preserved. You will need to re-enter the sync passphrase or set a new one. Continue?": "Αυτό διαγράφει οριστικά τα κρυπτογραφημένα διαπιστευτήρια που συγχρονίζουμε (π.χ. κωδικούς καταλόγου OPDS) σε κάθε συσκευή. Τα τοπικά αντίγραφα διατηρούνται. Θα χρειαστεί να εισαγάγετε ξανά τη φράση πρόσβασης συγχρονισμού ή να ορίσετε μια νέα. Συνέχεια;",
|
||||
"Sync passphrase forgotten — all encrypted fields cleared": "Η φράση πρόσβασης συγχρονισμού ξεχάστηκε — όλα τα κρυπτογραφημένα πεδία διαγράφηκαν",
|
||||
"Sync passphrase": "Φράση πρόσβασης συγχρονισμού",
|
||||
"Encrypts sensitive synced fields (like OPDS catalog credentials) before they leave your device. Set one now or wait — it will be requested when needed.": "Κρυπτογραφεί ευαίσθητα συγχρονισμένα πεδία (όπως διαπιστευτήρια καταλόγου OPDS) πριν φύγουν από τη συσκευή σας. Ορίστε μία τώρα ή περιμένετε — θα ζητηθεί όταν χρειαστεί.",
|
||||
"Set on this account. Will be requested when needed to decrypt synced credentials.": "Έχει οριστεί σε αυτόν τον λογαριασμό. Θα ζητηθεί όταν χρειαστεί για την αποκρυπτογράφηση συγχρονισμένων διαπιστευτηρίων.",
|
||||
"Set passphrase": "Ορισμός φράσης πρόσβασης",
|
||||
"Unlock now": "Ξεκλείδωμα τώρα",
|
||||
"Forgot passphrase": "Ξεχάσατε τη φράση πρόσβασης",
|
||||
"Incorrect PIN": "Λανθασμένο PIN",
|
||||
"App locked": "Η εφαρμογή είναι κλειδωμένη",
|
||||
"Enter your PIN": "Εισαγάγετε το PIN σας",
|
||||
"Readest is locked. Enter your 4-digit PIN to continue.": "Το Readest είναι κλειδωμένο. Εισαγάγετε το 4ψήφιο PIN σας για να συνεχίσετε.",
|
||||
"PIN code": "Κωδικός PIN",
|
||||
"Forgetting your PIN locks you out of this device. You'll need to clear the app's data to reset it.": "Αν ξεχάσετε το PIN σας, θα κλειδωθείτε από αυτή τη συσκευή. Θα χρειαστεί να διαγράψετε τα δεδομένα της εφαρμογής για να το επαναφέρετε.",
|
||||
"Passphrase must be at least 8 characters": "Η φράση πρόσβασης πρέπει να έχει τουλάχιστον 8 χαρακτήρες",
|
||||
"Passphrases do not match": "Οι φράσεις πρόσβασης δεν ταιριάζουν",
|
||||
"Set sync passphrase": "Ορισμός φράσης πρόσβασης συγχρονισμού",
|
||||
"Enter sync passphrase": "Εισαγωγή φράσης πρόσβασης συγχρονισμού",
|
||||
"A sync passphrase encrypts your sensitive fields (like OPDS catalog credentials) before they sync. We never see this passphrase. Pick something memorable — there is no recovery without it.": "Μια φράση πρόσβασης συγχρονισμού κρυπτογραφεί τα ευαίσθητα πεδία σας (όπως διαπιστευτήρια καταλόγου OPDS) πριν συγχρονιστούν. Δεν βλέπουμε ποτέ αυτή τη φράση πρόσβασης. Επιλέξτε κάτι αξιομνημόνευτο — δεν υπάρχει ανάκτηση χωρίς αυτή.",
|
||||
"Enter the sync passphrase you set on another device to decrypt your synced credentials.": "Εισαγάγετε τη φράση πρόσβασης συγχρονισμού που ορίσατε σε άλλη συσκευή για να αποκρυπτογραφήσετε τα συγχρονισμένα διαπιστευτήριά σας.",
|
||||
"Confirm passphrase": "Επιβεβαίωση φράσης πρόσβασης",
|
||||
"Unlock": "Ξεκλείδωμα",
|
||||
"Set PIN": "Ορισμός PIN",
|
||||
"Change PIN": "Αλλαγή PIN",
|
||||
"Disable PIN": "Απενεργοποίηση PIN",
|
||||
"Pick a 4-digit PIN. You will need to enter it every time you open Readest. There is no way to recover a forgotten PIN — clearing the app data is the only way to reset it.": "Επιλέξτε ένα 4ψήφιο PIN. Θα χρειάζεται να το εισάγετε κάθε φορά που ανοίγετε το Readest. Δεν υπάρχει τρόπος ανάκτησης ενός ξεχασμένου PIN — η διαγραφή των δεδομένων της εφαρμογής είναι ο μόνος τρόπος επαναφοράς.",
|
||||
"Enter your current PIN, then choose a new 4-digit PIN.": "Εισαγάγετε το τρέχον PIN σας και, στη συνέχεια, επιλέξτε ένα νέο 4ψήφιο PIN.",
|
||||
"Enter your current PIN to disable the app lock.": "Εισαγάγετε το τρέχον PIN σας για να απενεργοποιήσετε το κλείδωμα της εφαρμογής.",
|
||||
"PIN must be {{length}} digits": "Το PIN πρέπει να έχει {{length}} ψηφία",
|
||||
"PINs do not match": "Τα PIN δεν ταιριάζουν",
|
||||
"Current PIN": "Τρέχον PIN",
|
||||
"New PIN": "Νέο PIN",
|
||||
"Confirm new PIN": "Επιβεβαίωση νέου PIN"
|
||||
}
|
||||
|
||||
@@ -1364,5 +1364,43 @@
|
||||
"Failed to download file: {{title}}": "Error al descargar archivo: {{title}}",
|
||||
"Failed to delete cloud copy of the file: {{title}}": "Error al eliminar la copia en la nube del archivo: {{title}}",
|
||||
"{{percentage}}% used": "{{percentage}}% usado",
|
||||
"Resets in {{duration}}": "Se restablece en {{duration}}"
|
||||
"Resets in {{duration}}": "Se restablece en {{duration}}",
|
||||
"Set PIN…": "Establecer PIN…",
|
||||
"Require a 4-digit PIN to open Readest": "Requerir un PIN de 4 dígitos para abrir Readest",
|
||||
"Change PIN…": "Cambiar PIN…",
|
||||
"Disable PIN…": "Desactivar PIN…",
|
||||
"Sync passphrase ready": "Frase de contraseña de sincronización lista",
|
||||
"This permanently deletes the encrypted credentials we sync (e.g., OPDS catalog passwords) on every device. Local copies are preserved. You will need to re-enter the sync passphrase or set a new one. Continue?": "Esto elimina permanentemente las credenciales cifradas que sincronizamos (p. ej., contraseñas del catálogo OPDS) en cada dispositivo. Se conservan las copias locales. Tendrás que volver a introducir la frase de contraseña de sincronización o establecer una nueva. ¿Continuar?",
|
||||
"Sync passphrase forgotten — all encrypted fields cleared": "Frase de contraseña de sincronización olvidada — todos los campos cifrados se han borrado",
|
||||
"Sync passphrase": "Frase de contraseña de sincronización",
|
||||
"Encrypts sensitive synced fields (like OPDS catalog credentials) before they leave your device. Set one now or wait — it will be requested when needed.": "Cifra los campos sensibles sincronizados (como las credenciales del catálogo OPDS) antes de que salgan de tu dispositivo. Establece una ahora o espera — se solicitará cuando sea necesario.",
|
||||
"Set on this account. Will be requested when needed to decrypt synced credentials.": "Establecida en esta cuenta. Se solicitará cuando sea necesario descifrar las credenciales sincronizadas.",
|
||||
"Set passphrase": "Establecer frase de contraseña",
|
||||
"Unlock now": "Desbloquear ahora",
|
||||
"Forgot passphrase": "Olvidé la frase de contraseña",
|
||||
"Incorrect PIN": "PIN incorrecto",
|
||||
"App locked": "Aplicación bloqueada",
|
||||
"Enter your PIN": "Ingresa tu PIN",
|
||||
"Readest is locked. Enter your 4-digit PIN to continue.": "Readest está bloqueado. Ingresa tu PIN de 4 dígitos para continuar.",
|
||||
"PIN code": "Código PIN",
|
||||
"Forgetting your PIN locks you out of this device. You'll need to clear the app's data to reset it.": "Si olvidas tu PIN, te bloquearás en este dispositivo. Tendrás que borrar los datos de la aplicación para restablecerlo.",
|
||||
"Passphrase must be at least 8 characters": "La frase de contraseña debe tener al menos 8 caracteres",
|
||||
"Passphrases do not match": "Las frases de contraseña no coinciden",
|
||||
"Set sync passphrase": "Establecer frase de contraseña de sincronización",
|
||||
"Enter sync passphrase": "Introduce la frase de contraseña de sincronización",
|
||||
"A sync passphrase encrypts your sensitive fields (like OPDS catalog credentials) before they sync. We never see this passphrase. Pick something memorable — there is no recovery without it.": "Una frase de contraseña de sincronización cifra tus campos sensibles (como las credenciales del catálogo OPDS) antes de sincronizarlos. Nunca vemos esta frase de contraseña. Elige algo memorable — no hay recuperación sin ella.",
|
||||
"Enter the sync passphrase you set on another device to decrypt your synced credentials.": "Introduce la frase de contraseña de sincronización que estableciste en otro dispositivo para descifrar tus credenciales sincronizadas.",
|
||||
"Confirm passphrase": "Confirmar frase de contraseña",
|
||||
"Unlock": "Desbloquear",
|
||||
"Set PIN": "Establecer PIN",
|
||||
"Change PIN": "Cambiar PIN",
|
||||
"Disable PIN": "Desactivar PIN",
|
||||
"Pick a 4-digit PIN. You will need to enter it every time you open Readest. There is no way to recover a forgotten PIN — clearing the app data is the only way to reset it.": "Elige un PIN de 4 dígitos. Tendrás que introducirlo cada vez que abras Readest. No hay forma de recuperar un PIN olvidado — borrar los datos de la aplicación es la única forma de restablecerlo.",
|
||||
"Enter your current PIN, then choose a new 4-digit PIN.": "Ingresa tu PIN actual y, a continuación, elige un nuevo PIN de 4 dígitos.",
|
||||
"Enter your current PIN to disable the app lock.": "Ingresa tu PIN actual para desactivar el bloqueo de la aplicación.",
|
||||
"PIN must be {{length}} digits": "El PIN debe tener {{length}} dígitos",
|
||||
"PINs do not match": "Los PIN no coinciden",
|
||||
"Current PIN": "PIN actual",
|
||||
"New PIN": "Nuevo PIN",
|
||||
"Confirm new PIN": "Confirmar nuevo PIN"
|
||||
}
|
||||
|
||||
@@ -1340,5 +1340,43 @@
|
||||
"Failed to download file: {{title}}": "دانلود فایل «{{title}}» ناموفق بود.",
|
||||
"Failed to delete cloud copy of the file: {{title}}": "حذف نسخهی ابری فایل «{{title}}» ناموفق بود.",
|
||||
"{{percentage}}% used": "{{percentage}}٪ استفاده شده",
|
||||
"Resets in {{duration}}": "بازنشانی در {{duration}}"
|
||||
"Resets in {{duration}}": "بازنشانی در {{duration}}",
|
||||
"Set PIN…": "تنظیم PIN…",
|
||||
"Require a 4-digit PIN to open Readest": "برای باز کردن Readest یک PIN چهار رقمی الزامی شود",
|
||||
"Change PIN…": "تغییر PIN…",
|
||||
"Disable PIN…": "غیرفعال کردن PIN…",
|
||||
"Sync passphrase ready": "عبارت عبور همگامسازی آماده است",
|
||||
"This permanently deletes the encrypted credentials we sync (e.g., OPDS catalog passwords) on every device. Local copies are preserved. You will need to re-enter the sync passphrase or set a new one. Continue?": "این به طور دائم اعتبارنامههای رمزگذاریشدهای را که همگامسازی میکنیم (مانند رمزهای کاتالوگ OPDS) در همه دستگاهها حذف میکند. نسخههای محلی حفظ میشوند. باید عبارت عبور همگامسازی را دوباره وارد کنید یا یک عبارت جدید تنظیم کنید. ادامه میدهید؟",
|
||||
"Sync passphrase forgotten — all encrypted fields cleared": "عبارت عبور همگامسازی فراموش شد — تمام فیلدهای رمزگذاریشده پاک شدند",
|
||||
"Sync passphrase": "عبارت عبور همگامسازی",
|
||||
"Encrypts sensitive synced fields (like OPDS catalog credentials) before they leave your device. Set one now or wait — it will be requested when needed.": "فیلدهای حساس همگامسازیشده (مانند اعتبارنامههای کاتالوگ OPDS) را قبل از خروج از دستگاه شما رمزگذاری میکند. اکنون یکی تنظیم کنید یا منتظر بمانید — هنگام نیاز درخواست خواهد شد.",
|
||||
"Set on this account. Will be requested when needed to decrypt synced credentials.": "در این حساب تنظیم شده است. هنگام نیاز برای رمزگشایی اعتبارنامههای همگامسازیشده درخواست خواهد شد.",
|
||||
"Set passphrase": "تنظیم عبارت عبور",
|
||||
"Unlock now": "اکنون باز کن",
|
||||
"Forgot passphrase": "عبارت عبور را فراموش کردهاید",
|
||||
"Incorrect PIN": "PIN نادرست",
|
||||
"App locked": "برنامه قفل است",
|
||||
"Enter your PIN": "PIN خود را وارد کنید",
|
||||
"Readest is locked. Enter your 4-digit PIN to continue.": "Readest قفل است. برای ادامه، PIN چهار رقمی خود را وارد کنید.",
|
||||
"PIN code": "کد PIN",
|
||||
"Forgetting your PIN locks you out of this device. You'll need to clear the app's data to reset it.": "فراموش کردن PIN باعث میشود از این دستگاه خارج شوید. برای بازنشانی آن باید دادههای برنامه را پاک کنید.",
|
||||
"Passphrase must be at least 8 characters": "عبارت عبور باید حداقل ۸ نویسه باشد",
|
||||
"Passphrases do not match": "عبارتهای عبور مطابقت ندارند",
|
||||
"Set sync passphrase": "تنظیم عبارت عبور همگامسازی",
|
||||
"Enter sync passphrase": "عبارت عبور همگامسازی را وارد کنید",
|
||||
"A sync passphrase encrypts your sensitive fields (like OPDS catalog credentials) before they sync. We never see this passphrase. Pick something memorable — there is no recovery without it.": "عبارت عبور همگامسازی، فیلدهای حساس شما (مانند اعتبارنامههای کاتالوگ OPDS) را قبل از همگامسازی رمزگذاری میکند. ما هرگز این عبارت عبور را نمیبینیم. چیزی بهیادماندنی انتخاب کنید — بدون آن هیچ راه بازیابی وجود ندارد.",
|
||||
"Enter the sync passphrase you set on another device to decrypt your synced credentials.": "برای رمزگشایی اعتبارنامههای همگامسازیشده، عبارت عبور همگامسازی که در دستگاه دیگری تنظیم کردهاید را وارد کنید.",
|
||||
"Confirm passphrase": "تأیید عبارت عبور",
|
||||
"Unlock": "باز کردن",
|
||||
"Set PIN": "تنظیم PIN",
|
||||
"Change PIN": "تغییر PIN",
|
||||
"Disable PIN": "غیرفعال کردن PIN",
|
||||
"Pick a 4-digit PIN. You will need to enter it every time you open Readest. There is no way to recover a forgotten PIN — clearing the app data is the only way to reset it.": "یک PIN چهار رقمی انتخاب کنید. هر بار که Readest را باز میکنید باید آن را وارد کنید. هیچ راهی برای بازیابی PIN فراموششده وجود ندارد — پاک کردن دادههای برنامه تنها راه بازنشانی آن است.",
|
||||
"Enter your current PIN, then choose a new 4-digit PIN.": "PIN فعلی خود را وارد کنید و سپس یک PIN چهار رقمی جدید انتخاب کنید.",
|
||||
"Enter your current PIN to disable the app lock.": "برای غیرفعال کردن قفل برنامه، PIN فعلی خود را وارد کنید.",
|
||||
"PIN must be {{length}} digits": "PIN باید {{length}} رقم باشد",
|
||||
"PINs do not match": "PINها مطابقت ندارند",
|
||||
"Current PIN": "PIN فعلی",
|
||||
"New PIN": "PIN جدید",
|
||||
"Confirm new PIN": "تأیید PIN جدید"
|
||||
}
|
||||
|
||||
@@ -1364,5 +1364,43 @@
|
||||
"Failed to download file: {{title}}": "Échec du téléchargement du fichier : {{title}}",
|
||||
"Failed to delete cloud copy of the file: {{title}}": "Échec de la suppression de la copie cloud du fichier : {{title}}",
|
||||
"{{percentage}}% used": "{{percentage}} % utilisés",
|
||||
"Resets in {{duration}}": "Réinitialisation dans {{duration}}"
|
||||
"Resets in {{duration}}": "Réinitialisation dans {{duration}}",
|
||||
"Set PIN…": "Définir le PIN…",
|
||||
"Require a 4-digit PIN to open Readest": "Exiger un PIN à 4 chiffres pour ouvrir Readest",
|
||||
"Change PIN…": "Modifier le PIN…",
|
||||
"Disable PIN…": "Désactiver le PIN…",
|
||||
"Sync passphrase ready": "Phrase secrète de synchronisation prête",
|
||||
"This permanently deletes the encrypted credentials we sync (e.g., OPDS catalog passwords) on every device. Local copies are preserved. You will need to re-enter the sync passphrase or set a new one. Continue?": "Cela supprime définitivement les identifiants chiffrés que nous synchronisons (par exemple, les mots de passe du catalogue OPDS) sur chaque appareil. Les copies locales sont conservées. Vous devrez ressaisir la phrase secrète de synchronisation ou en définir une nouvelle. Continuer ?",
|
||||
"Sync passphrase forgotten — all encrypted fields cleared": "Phrase secrète de synchronisation oubliée — tous les champs chiffrés ont été effacés",
|
||||
"Sync passphrase": "Phrase secrète de synchronisation",
|
||||
"Encrypts sensitive synced fields (like OPDS catalog credentials) before they leave your device. Set one now or wait — it will be requested when needed.": "Chiffre les champs synchronisés sensibles (comme les identifiants du catalogue OPDS) avant qu'ils ne quittent votre appareil. Définissez-en une maintenant ou attendez — elle sera demandée si nécessaire.",
|
||||
"Set on this account. Will be requested when needed to decrypt synced credentials.": "Définie pour ce compte. Sera demandée lorsque nécessaire pour déchiffrer les identifiants synchronisés.",
|
||||
"Set passphrase": "Définir la phrase secrète",
|
||||
"Unlock now": "Déverrouiller maintenant",
|
||||
"Forgot passphrase": "Phrase secrète oubliée",
|
||||
"Incorrect PIN": "PIN incorrect",
|
||||
"App locked": "Application verrouillée",
|
||||
"Enter your PIN": "Saisissez votre PIN",
|
||||
"Readest is locked. Enter your 4-digit PIN to continue.": "Readest est verrouillé. Saisissez votre PIN à 4 chiffres pour continuer.",
|
||||
"PIN code": "Code PIN",
|
||||
"Forgetting your PIN locks you out of this device. You'll need to clear the app's data to reset it.": "Si vous oubliez votre PIN, vous serez verrouillé hors de cet appareil. Vous devrez effacer les données de l'application pour le réinitialiser.",
|
||||
"Passphrase must be at least 8 characters": "La phrase secrète doit comporter au moins 8 caractères",
|
||||
"Passphrases do not match": "Les phrases secrètes ne correspondent pas",
|
||||
"Set sync passphrase": "Définir la phrase secrète de synchronisation",
|
||||
"Enter sync passphrase": "Saisir la phrase secrète de synchronisation",
|
||||
"A sync passphrase encrypts your sensitive fields (like OPDS catalog credentials) before they sync. We never see this passphrase. Pick something memorable — there is no recovery without it.": "Une phrase secrète de synchronisation chiffre vos champs sensibles (comme les identifiants du catalogue OPDS) avant qu'ils ne soient synchronisés. Nous ne voyons jamais cette phrase secrète. Choisissez quelque chose de mémorable — il n'y a pas de récupération sans elle.",
|
||||
"Enter the sync passphrase you set on another device to decrypt your synced credentials.": "Saisissez la phrase secrète de synchronisation que vous avez définie sur un autre appareil pour déchiffrer vos identifiants synchronisés.",
|
||||
"Confirm passphrase": "Confirmer la phrase secrète",
|
||||
"Unlock": "Déverrouiller",
|
||||
"Set PIN": "Définir le PIN",
|
||||
"Change PIN": "Modifier le PIN",
|
||||
"Disable PIN": "Désactiver le PIN",
|
||||
"Pick a 4-digit PIN. You will need to enter it every time you open Readest. There is no way to recover a forgotten PIN — clearing the app data is the only way to reset it.": "Choisissez un PIN à 4 chiffres. Vous devrez le saisir chaque fois que vous ouvrirez Readest. Il n'existe aucun moyen de récupérer un PIN oublié — effacer les données de l'application est le seul moyen de le réinitialiser.",
|
||||
"Enter your current PIN, then choose a new 4-digit PIN.": "Saisissez votre PIN actuel, puis choisissez un nouveau PIN à 4 chiffres.",
|
||||
"Enter your current PIN to disable the app lock.": "Saisissez votre PIN actuel pour désactiver le verrouillage de l'application.",
|
||||
"PIN must be {{length}} digits": "Le PIN doit comporter {{length}} chiffres",
|
||||
"PINs do not match": "Les PIN ne correspondent pas",
|
||||
"Current PIN": "PIN actuel",
|
||||
"New PIN": "Nouveau PIN",
|
||||
"Confirm new PIN": "Confirmer le nouveau PIN"
|
||||
}
|
||||
|
||||
@@ -1364,5 +1364,43 @@
|
||||
"Failed to download file: {{title}}": "הורדת הקובץ נכשלה: {{title}}",
|
||||
"Failed to delete cloud copy of the file: {{title}}": "מחיקת עותק הענן של הקובץ נכשלה: {{title}}",
|
||||
"{{percentage}}% used": "נוצלו {{percentage}}%",
|
||||
"Resets in {{duration}}": "מתאפס בעוד {{duration}}"
|
||||
"Resets in {{duration}}": "מתאפס בעוד {{duration}}",
|
||||
"Set PIN…": "הגדר PIN…",
|
||||
"Require a 4-digit PIN to open Readest": "דרוש PIN בן 4 ספרות כדי לפתוח את Readest",
|
||||
"Change PIN…": "שנה PIN…",
|
||||
"Disable PIN…": "השבת PIN…",
|
||||
"Sync passphrase ready": "ביטוי הסיסמה לסנכרון מוכן",
|
||||
"This permanently deletes the encrypted credentials we sync (e.g., OPDS catalog passwords) on every device. Local copies are preserved. You will need to re-enter the sync passphrase or set a new one. Continue?": "פעולה זו מוחקת לצמיתות את פרטי ההתחברות המוצפנים שאנו מסנכרנים (למשל, סיסמאות קטלוג OPDS) בכל מכשיר. עותקים מקומיים נשמרים. תצטרך להזין מחדש את ביטוי הסיסמה לסנכרון או להגדיר אחד חדש. להמשיך?",
|
||||
"Sync passphrase forgotten — all encrypted fields cleared": "ביטוי הסיסמה לסנכרון נשכח — כל השדות המוצפנים נמחקו",
|
||||
"Sync passphrase": "ביטוי סיסמה לסנכרון",
|
||||
"Encrypts sensitive synced fields (like OPDS catalog credentials) before they leave your device. Set one now or wait — it will be requested when needed.": "מצפין שדות מסונכרנים רגישים (כמו פרטי התחברות לקטלוג OPDS) לפני שהם עוזבים את המכשיר שלך. הגדר ביטוי עכשיו או המתן — נבקש אותו כשיידרש.",
|
||||
"Set on this account. Will be requested when needed to decrypt synced credentials.": "הוגדר בחשבון זה. נבקש אותו כשיידרש לפענח פרטי התחברות מסונכרנים.",
|
||||
"Set passphrase": "הגדרת ביטוי סיסמה",
|
||||
"Unlock now": "פתח כעת",
|
||||
"Forgot passphrase": "שכחת את ביטוי הסיסמה",
|
||||
"Incorrect PIN": "PIN שגוי",
|
||||
"App locked": "האפליקציה נעולה",
|
||||
"Enter your PIN": "הזן את ה-PIN שלך",
|
||||
"Readest is locked. Enter your 4-digit PIN to continue.": "Readest נעול. הזן את ה-PIN בן 4 הספרות שלך כדי להמשיך.",
|
||||
"PIN code": "קוד PIN",
|
||||
"Forgetting your PIN locks you out of this device. You'll need to clear the app's data to reset it.": "שכחת ה-PIN תנעל אותך מחוץ למכשיר זה. תצטרך לנקות את נתוני האפליקציה כדי לאפס אותו.",
|
||||
"Passphrase must be at least 8 characters": "ביטוי הסיסמה חייב להכיל לפחות 8 תווים",
|
||||
"Passphrases do not match": "ביטויי הסיסמה אינם תואמים",
|
||||
"Set sync passphrase": "הגדרת ביטוי סיסמה לסנכרון",
|
||||
"Enter sync passphrase": "הזן ביטוי סיסמה לסנכרון",
|
||||
"A sync passphrase encrypts your sensitive fields (like OPDS catalog credentials) before they sync. We never see this passphrase. Pick something memorable — there is no recovery without it.": "ביטוי סיסמה לסנכרון מצפין את השדות הרגישים שלך (כמו פרטי התחברות לקטלוג OPDS) לפני שהם מסונכרנים. אנו לעולם לא רואים את ביטוי הסיסמה הזה. בחר משהו זכיר — אין שחזור בלעדיו.",
|
||||
"Enter the sync passphrase you set on another device to decrypt your synced credentials.": "הזן את ביטוי הסיסמה לסנכרון שהגדרת במכשיר אחר כדי לפענח את פרטי ההתחברות המסונכרנים שלך.",
|
||||
"Confirm passphrase": "אישור ביטוי הסיסמה",
|
||||
"Unlock": "פתח",
|
||||
"Set PIN": "הגדר PIN",
|
||||
"Change PIN": "שנה PIN",
|
||||
"Disable PIN": "השבת PIN",
|
||||
"Pick a 4-digit PIN. You will need to enter it every time you open Readest. There is no way to recover a forgotten PIN — clearing the app data is the only way to reset it.": "בחר PIN בן 4 ספרות. תצטרך להזין אותו בכל פעם שתפתח את Readest. אין דרך לשחזר PIN שנשכח — ניקוי נתוני האפליקציה הוא הדרך היחידה לאפס אותו.",
|
||||
"Enter your current PIN, then choose a new 4-digit PIN.": "הזן את ה-PIN הנוכחי שלך, ולאחר מכן בחר PIN חדש בן 4 ספרות.",
|
||||
"Enter your current PIN to disable the app lock.": "הזן את ה-PIN הנוכחי שלך כדי להשבית את נעילת האפליקציה.",
|
||||
"PIN must be {{length}} digits": "ה-PIN חייב להיות בן {{length}} ספרות",
|
||||
"PINs do not match": "ה-PINs אינם תואמים",
|
||||
"Current PIN": "PIN נוכחי",
|
||||
"New PIN": "PIN חדש",
|
||||
"Confirm new PIN": "אשר PIN חדש"
|
||||
}
|
||||
|
||||
@@ -1340,5 +1340,43 @@
|
||||
"Failed to download file: {{title}}": "फ़ाइल डाउनलोड करने में विफल: {{title}}",
|
||||
"Failed to delete cloud copy of the file: {{title}}": "फ़ाइल की क्लाउड प्रति हटाने में विफल: {{title}}",
|
||||
"{{percentage}}% used": "{{percentage}}% उपयोग किया गया",
|
||||
"Resets in {{duration}}": "{{duration}} में रीसेट होगा"
|
||||
"Resets in {{duration}}": "{{duration}} में रीसेट होगा",
|
||||
"Set PIN…": "PIN सेट करें…",
|
||||
"Require a 4-digit PIN to open Readest": "Readest खोलने के लिए 4-अंकों का PIN आवश्यक करें",
|
||||
"Change PIN…": "PIN बदलें…",
|
||||
"Disable PIN…": "PIN अक्षम करें…",
|
||||
"Sync passphrase ready": "सिंक पासफ़्रेज़ तैयार",
|
||||
"This permanently deletes the encrypted credentials we sync (e.g., OPDS catalog passwords) on every device. Local copies are preserved. You will need to re-enter the sync passphrase or set a new one. Continue?": "यह हर डिवाइस पर हमारे सिंक किए गए एन्क्रिप्टेड क्रेडेंशियल (जैसे, OPDS कैटलॉग पासवर्ड) को स्थायी रूप से हटा देता है। स्थानीय प्रतियां सुरक्षित रहती हैं। आपको सिंक पासफ़्रेज़ फिर से दर्ज करना होगा या एक नया सेट करना होगा। जारी रखें?",
|
||||
"Sync passphrase forgotten — all encrypted fields cleared": "सिंक पासफ़्रेज़ भूल गए — सभी एन्क्रिप्टेड फ़ील्ड हटा दिए गए",
|
||||
"Sync passphrase": "सिंक पासफ़्रेज़",
|
||||
"Encrypts sensitive synced fields (like OPDS catalog credentials) before they leave your device. Set one now or wait — it will be requested when needed.": "संवेदनशील सिंक किए गए फ़ील्ड (जैसे OPDS कैटलॉग क्रेडेंशियल) को आपके डिवाइस से बाहर जाने से पहले एन्क्रिप्ट करता है। अभी एक सेट करें या प्रतीक्षा करें — आवश्यकता पड़ने पर इसका अनुरोध किया जाएगा।",
|
||||
"Set on this account. Will be requested when needed to decrypt synced credentials.": "इस खाते पर सेट किया गया है। सिंक किए गए क्रेडेंशियल को डिक्रिप्ट करने के लिए आवश्यकता पड़ने पर इसका अनुरोध किया जाएगा।",
|
||||
"Set passphrase": "पासफ़्रेज़ सेट करें",
|
||||
"Unlock now": "अभी अनलॉक करें",
|
||||
"Forgot passphrase": "पासफ़्रेज़ भूल गए",
|
||||
"Incorrect PIN": "गलत PIN",
|
||||
"App locked": "ऐप लॉक है",
|
||||
"Enter your PIN": "अपना PIN दर्ज करें",
|
||||
"Readest is locked. Enter your 4-digit PIN to continue.": "Readest लॉक है। जारी रखने के लिए अपना 4-अंकों का PIN दर्ज करें।",
|
||||
"PIN code": "PIN कोड",
|
||||
"Forgetting your PIN locks you out of this device. You'll need to clear the app's data to reset it.": "PIN भूलने पर आप इस डिवाइस से बाहर हो जाएंगे। इसे रीसेट करने के लिए आपको ऐप का डेटा मिटाना होगा।",
|
||||
"Passphrase must be at least 8 characters": "पासफ़्रेज़ कम से कम 8 वर्णों का होना चाहिए",
|
||||
"Passphrases do not match": "पासफ़्रेज़ मेल नहीं खाते",
|
||||
"Set sync passphrase": "सिंक पासफ़्रेज़ सेट करें",
|
||||
"Enter sync passphrase": "सिंक पासफ़्रेज़ दर्ज करें",
|
||||
"A sync passphrase encrypts your sensitive fields (like OPDS catalog credentials) before they sync. We never see this passphrase. Pick something memorable — there is no recovery without it.": "एक सिंक पासफ़्रेज़ आपके संवेदनशील फ़ील्ड (जैसे OPDS कैटलॉग क्रेडेंशियल) को सिंक होने से पहले एन्क्रिप्ट करता है। हम यह पासफ़्रेज़ कभी नहीं देखते। ऐसा कुछ चुनें जो याद रहे — इसके बिना कोई पुनर्प्राप्ति नहीं है।",
|
||||
"Enter the sync passphrase you set on another device to decrypt your synced credentials.": "अपने सिंक किए गए क्रेडेंशियल को डिक्रिप्ट करने के लिए वह सिंक पासफ़्रेज़ दर्ज करें जो आपने किसी अन्य डिवाइस पर सेट किया था।",
|
||||
"Confirm passphrase": "पासफ़्रेज़ की पुष्टि करें",
|
||||
"Unlock": "अनलॉक करें",
|
||||
"Set PIN": "PIN सेट करें",
|
||||
"Change PIN": "PIN बदलें",
|
||||
"Disable PIN": "PIN अक्षम करें",
|
||||
"Pick a 4-digit PIN. You will need to enter it every time you open Readest. There is no way to recover a forgotten PIN — clearing the app data is the only way to reset it.": "एक 4-अंकों का PIN चुनें। आपको हर बार Readest खोलने पर इसे दर्ज करना होगा। भूले हुए PIN को पुनर्प्राप्त करने का कोई तरीका नहीं है — ऐप डेटा साफ़ करना ही इसे रीसेट करने का एकमात्र तरीका है।",
|
||||
"Enter your current PIN, then choose a new 4-digit PIN.": "अपना वर्तमान PIN दर्ज करें, फिर एक नया 4-अंकों का PIN चुनें।",
|
||||
"Enter your current PIN to disable the app lock.": "ऐप लॉक अक्षम करने के लिए अपना वर्तमान PIN दर्ज करें।",
|
||||
"PIN must be {{length}} digits": "PIN {{length}} अंकों का होना चाहिए",
|
||||
"PINs do not match": "PIN मेल नहीं खाते",
|
||||
"Current PIN": "वर्तमान PIN",
|
||||
"New PIN": "नया PIN",
|
||||
"Confirm new PIN": "नए PIN की पुष्टि करें"
|
||||
}
|
||||
|
||||
@@ -1340,5 +1340,43 @@
|
||||
"Failed to download file: {{title}}": "Fájl letöltése sikertelen: {{title}}",
|
||||
"Failed to delete cloud copy of the file: {{title}}": "Fájl felhőmásolatának törlése sikertelen: {{title}}",
|
||||
"{{percentage}}% used": "{{percentage}}% felhasználva",
|
||||
"Resets in {{duration}}": "Visszaáll {{duration}} múlva"
|
||||
"Resets in {{duration}}": "Visszaáll {{duration}} múlva",
|
||||
"Set PIN…": "PIN beállítása…",
|
||||
"Require a 4-digit PIN to open Readest": "4 jegyű PIN megkövetelése a Readest megnyitásához",
|
||||
"Change PIN…": "PIN módosítása…",
|
||||
"Disable PIN…": "PIN letiltása…",
|
||||
"Sync passphrase ready": "Szinkronizálási jelmondat készen áll",
|
||||
"This permanently deletes the encrypted credentials we sync (e.g., OPDS catalog passwords) on every device. Local copies are preserved. You will need to re-enter the sync passphrase or set a new one. Continue?": "Ez véglegesen törli a szinkronizált titkosított hitelesítő adatokat (pl. OPDS-katalógusjelszavak) minden eszközön. A helyi másolatok megmaradnak. Újra meg kell adnia a szinkronizálási jelmondatot, vagy újat kell beállítania. Folytatja?",
|
||||
"Sync passphrase forgotten — all encrypted fields cleared": "Szinkronizálási jelmondat elfelejtve — az összes titkosított mező törölve",
|
||||
"Sync passphrase": "Szinkronizálási jelmondat",
|
||||
"Encrypts sensitive synced fields (like OPDS catalog credentials) before they leave your device. Set one now or wait — it will be requested when needed.": "Titkosítja az érzékeny szinkronizált mezőket (például OPDS-katalógus hitelesítő adatait), mielőtt elhagyják az eszközét. Állítson be egyet most vagy várjon — szükség esetén kérni fogjuk.",
|
||||
"Set on this account. Will be requested when needed to decrypt synced credentials.": "Beállítva ezen a fiókon. Szükség esetén kérni fogjuk a szinkronizált hitelesítő adatok visszafejtéséhez.",
|
||||
"Set passphrase": "Jelmondat beállítása",
|
||||
"Unlock now": "Feloldás most",
|
||||
"Forgot passphrase": "Elfelejtett jelmondat",
|
||||
"Incorrect PIN": "Helytelen PIN",
|
||||
"App locked": "Az alkalmazás zárolva van",
|
||||
"Enter your PIN": "Adja meg a PIN-kódját",
|
||||
"Readest is locked. Enter your 4-digit PIN to continue.": "A Readest zárolva van. A folytatáshoz adja meg a 4 jegyű PIN-kódját.",
|
||||
"PIN code": "PIN-kód",
|
||||
"Forgetting your PIN locks you out of this device. You'll need to clear the app's data to reset it.": "Ha elfelejti a PIN-kódját, kizárja magát erről az eszközről. Az alaphelyzetbe állításhoz törölnie kell az alkalmazás adatait.",
|
||||
"Passphrase must be at least 8 characters": "A jelmondatnak legalább 8 karakter hosszúnak kell lennie",
|
||||
"Passphrases do not match": "A jelmondatok nem egyeznek",
|
||||
"Set sync passphrase": "Szinkronizálási jelmondat beállítása",
|
||||
"Enter sync passphrase": "Adja meg a szinkronizálási jelmondatot",
|
||||
"A sync passphrase encrypts your sensitive fields (like OPDS catalog credentials) before they sync. We never see this passphrase. Pick something memorable — there is no recovery without it.": "A szinkronizálási jelmondat titkosítja az érzékeny mezőket (mint például az OPDS-katalógus hitelesítő adatait), mielőtt szinkronizálódnának. Soha nem látjuk ezt a jelmondatot. Válasszon valami emlékezeteset — nélküle nincs helyreállítás.",
|
||||
"Enter the sync passphrase you set on another device to decrypt your synced credentials.": "Adja meg a másik eszközön beállított szinkronizálási jelmondatot a szinkronizált hitelesítő adatok visszafejtéséhez.",
|
||||
"Confirm passphrase": "Jelmondat megerősítése",
|
||||
"Unlock": "Feloldás",
|
||||
"Set PIN": "PIN beállítása",
|
||||
"Change PIN": "PIN módosítása",
|
||||
"Disable PIN": "PIN letiltása",
|
||||
"Pick a 4-digit PIN. You will need to enter it every time you open Readest. There is no way to recover a forgotten PIN — clearing the app data is the only way to reset it.": "Válasszon egy 4 jegyű PIN-kódot. Minden alkalommal meg kell adnia, amikor megnyitja a Readestet. Az elfelejtett PIN-kódot nem lehet helyreállítani — az alkalmazás adatainak törlése az egyetlen módja az alaphelyzetbe állításnak.",
|
||||
"Enter your current PIN, then choose a new 4-digit PIN.": "Adja meg a jelenlegi PIN-kódját, majd válasszon egy új 4 jegyű PIN-kódot.",
|
||||
"Enter your current PIN to disable the app lock.": "Az alkalmazászár letiltásához adja meg a jelenlegi PIN-kódját.",
|
||||
"PIN must be {{length}} digits": "A PIN-kódnak {{length}} jegyűnek kell lennie",
|
||||
"PINs do not match": "A PIN-kódok nem egyeznek",
|
||||
"Current PIN": "Jelenlegi PIN",
|
||||
"New PIN": "Új PIN",
|
||||
"Confirm new PIN": "Új PIN megerősítése"
|
||||
}
|
||||
|
||||
@@ -1316,5 +1316,43 @@
|
||||
"Failed to download file: {{title}}": "Gagal mengunduh file: {{title}}",
|
||||
"Failed to delete cloud copy of the file: {{title}}": "Gagal menghapus salinan cloud file: {{title}}",
|
||||
"{{percentage}}% used": "{{percentage}}% terpakai",
|
||||
"Resets in {{duration}}": "Disetel ulang dalam {{duration}}"
|
||||
"Resets in {{duration}}": "Disetel ulang dalam {{duration}}",
|
||||
"Set PIN…": "Atur PIN…",
|
||||
"Require a 4-digit PIN to open Readest": "Wajibkan PIN 4 digit untuk membuka Readest",
|
||||
"Change PIN…": "Ubah PIN…",
|
||||
"Disable PIN…": "Nonaktifkan PIN…",
|
||||
"Sync passphrase ready": "Frasa sandi sinkronisasi siap",
|
||||
"This permanently deletes the encrypted credentials we sync (e.g., OPDS catalog passwords) on every device. Local copies are preserved. You will need to re-enter the sync passphrase or set a new one. Continue?": "Ini secara permanen menghapus kredensial terenkripsi yang kami sinkronkan (mis., kata sandi katalog OPDS) di setiap perangkat. Salinan lokal disimpan. Anda perlu memasukkan kembali frasa sandi sinkronisasi atau menyetel yang baru. Lanjutkan?",
|
||||
"Sync passphrase forgotten — all encrypted fields cleared": "Frasa sandi sinkronisasi terlupakan — semua bidang terenkripsi dihapus",
|
||||
"Sync passphrase": "Frasa sandi sinkronisasi",
|
||||
"Encrypts sensitive synced fields (like OPDS catalog credentials) before they leave your device. Set one now or wait — it will be requested when needed.": "Mengenkripsi bidang tersinkron yang sensitif (seperti kredensial katalog OPDS) sebelum meninggalkan perangkat Anda. Setel sekarang atau tunggu — akan diminta saat dibutuhkan.",
|
||||
"Set on this account. Will be requested when needed to decrypt synced credentials.": "Disetel pada akun ini. Akan diminta saat dibutuhkan untuk mendekripsi kredensial yang tersinkron.",
|
||||
"Set passphrase": "Setel frasa sandi",
|
||||
"Unlock now": "Buka kunci sekarang",
|
||||
"Forgot passphrase": "Lupa frasa sandi",
|
||||
"Incorrect PIN": "PIN salah",
|
||||
"App locked": "Aplikasi terkunci",
|
||||
"Enter your PIN": "Masukkan PIN Anda",
|
||||
"Readest is locked. Enter your 4-digit PIN to continue.": "Readest terkunci. Masukkan PIN 4 digit Anda untuk melanjutkan.",
|
||||
"PIN code": "Kode PIN",
|
||||
"Forgetting your PIN locks you out of this device. You'll need to clear the app's data to reset it.": "Lupa PIN akan mengunci Anda dari perangkat ini. Anda perlu menghapus data aplikasi untuk meresetnya.",
|
||||
"Passphrase must be at least 8 characters": "Frasa sandi harus minimal 8 karakter",
|
||||
"Passphrases do not match": "Frasa sandi tidak cocok",
|
||||
"Set sync passphrase": "Setel frasa sandi sinkronisasi",
|
||||
"Enter sync passphrase": "Masukkan frasa sandi sinkronisasi",
|
||||
"A sync passphrase encrypts your sensitive fields (like OPDS catalog credentials) before they sync. We never see this passphrase. Pick something memorable — there is no recovery without it.": "Frasa sandi sinkronisasi mengenkripsi bidang sensitif Anda (seperti kredensial katalog OPDS) sebelum disinkronkan. Kami tidak pernah melihat frasa sandi ini. Pilih sesuatu yang mudah diingat — tidak ada pemulihan tanpanya.",
|
||||
"Enter the sync passphrase you set on another device to decrypt your synced credentials.": "Masukkan frasa sandi sinkronisasi yang Anda setel di perangkat lain untuk mendekripsi kredensial yang tersinkron.",
|
||||
"Confirm passphrase": "Konfirmasi frasa sandi",
|
||||
"Unlock": "Buka kunci",
|
||||
"Set PIN": "Atur PIN",
|
||||
"Change PIN": "Ubah PIN",
|
||||
"Disable PIN": "Nonaktifkan PIN",
|
||||
"Pick a 4-digit PIN. You will need to enter it every time you open Readest. There is no way to recover a forgotten PIN — clearing the app data is the only way to reset it.": "Pilih PIN 4 digit. Anda perlu memasukkannya setiap kali membuka Readest. Tidak ada cara untuk memulihkan PIN yang terlupakan — menghapus data aplikasi adalah satu-satunya cara untuk meresetnya.",
|
||||
"Enter your current PIN, then choose a new 4-digit PIN.": "Masukkan PIN Anda saat ini, lalu pilih PIN 4 digit baru.",
|
||||
"Enter your current PIN to disable the app lock.": "Masukkan PIN Anda saat ini untuk menonaktifkan kunci aplikasi.",
|
||||
"PIN must be {{length}} digits": "PIN harus terdiri dari {{length}} digit",
|
||||
"PINs do not match": "PIN tidak cocok",
|
||||
"Current PIN": "PIN saat ini",
|
||||
"New PIN": "PIN baru",
|
||||
"Confirm new PIN": "Konfirmasi PIN baru"
|
||||
}
|
||||
|
||||
@@ -1364,5 +1364,43 @@
|
||||
"Failed to download file: {{title}}": "Download file non riuscito: {{title}}",
|
||||
"Failed to delete cloud copy of the file: {{title}}": "Impossibile eliminare la copia cloud del file: {{title}}",
|
||||
"{{percentage}}% used": "{{percentage}}% utilizzato",
|
||||
"Resets in {{duration}}": "Si reimposta tra {{duration}}"
|
||||
"Resets in {{duration}}": "Si reimposta tra {{duration}}",
|
||||
"Set PIN…": "Imposta PIN…",
|
||||
"Require a 4-digit PIN to open Readest": "Richiedi un PIN di 4 cifre per aprire Readest",
|
||||
"Change PIN…": "Cambia PIN…",
|
||||
"Disable PIN…": "Disattiva PIN…",
|
||||
"Sync passphrase ready": "Passphrase di sincronizzazione pronta",
|
||||
"This permanently deletes the encrypted credentials we sync (e.g., OPDS catalog passwords) on every device. Local copies are preserved. You will need to re-enter the sync passphrase or set a new one. Continue?": "Questa operazione elimina in modo permanente le credenziali crittografate che sincronizziamo (ad es. le password del catalogo OPDS) su ogni dispositivo. Le copie locali vengono conservate. Dovrai reinserire la passphrase di sincronizzazione o impostarne una nuova. Continuare?",
|
||||
"Sync passphrase forgotten — all encrypted fields cleared": "Passphrase di sincronizzazione dimenticata — tutti i campi crittografati cancellati",
|
||||
"Sync passphrase": "Passphrase di sincronizzazione",
|
||||
"Encrypts sensitive synced fields (like OPDS catalog credentials) before they leave your device. Set one now or wait — it will be requested when needed.": "Crittografa i campi sincronizzati sensibili (come le credenziali del catalogo OPDS) prima che lascino il tuo dispositivo. Impostane una ora o attendi — verrà richiesta quando necessario.",
|
||||
"Set on this account. Will be requested when needed to decrypt synced credentials.": "Impostata su questo account. Verrà richiesta quando necessario per decrittografare le credenziali sincronizzate.",
|
||||
"Set passphrase": "Imposta passphrase",
|
||||
"Unlock now": "Sblocca ora",
|
||||
"Forgot passphrase": "Passphrase dimenticata",
|
||||
"Incorrect PIN": "PIN errato",
|
||||
"App locked": "App bloccata",
|
||||
"Enter your PIN": "Inserisci il tuo PIN",
|
||||
"Readest is locked. Enter your 4-digit PIN to continue.": "Readest è bloccato. Inserisci il tuo PIN di 4 cifre per continuare.",
|
||||
"PIN code": "Codice PIN",
|
||||
"Forgetting your PIN locks you out of this device. You'll need to clear the app's data to reset it.": "Dimenticare il PIN ti escluderà da questo dispositivo. Dovrai cancellare i dati dell'app per reimpostarlo.",
|
||||
"Passphrase must be at least 8 characters": "La passphrase deve contenere almeno 8 caratteri",
|
||||
"Passphrases do not match": "Le passphrase non corrispondono",
|
||||
"Set sync passphrase": "Imposta passphrase di sincronizzazione",
|
||||
"Enter sync passphrase": "Inserisci la passphrase di sincronizzazione",
|
||||
"A sync passphrase encrypts your sensitive fields (like OPDS catalog credentials) before they sync. We never see this passphrase. Pick something memorable — there is no recovery without it.": "Una passphrase di sincronizzazione crittografa i tuoi campi sensibili (come le credenziali del catalogo OPDS) prima che vengano sincronizzati. Non vediamo mai questa passphrase. Scegli qualcosa di memorabile — senza di essa non c'è possibilità di recupero.",
|
||||
"Enter the sync passphrase you set on another device to decrypt your synced credentials.": "Inserisci la passphrase di sincronizzazione impostata su un altro dispositivo per decrittografare le tue credenziali sincronizzate.",
|
||||
"Confirm passphrase": "Conferma passphrase",
|
||||
"Unlock": "Sblocca",
|
||||
"Set PIN": "Imposta PIN",
|
||||
"Change PIN": "Cambia PIN",
|
||||
"Disable PIN": "Disattiva PIN",
|
||||
"Pick a 4-digit PIN. You will need to enter it every time you open Readest. There is no way to recover a forgotten PIN — clearing the app data is the only way to reset it.": "Scegli un PIN di 4 cifre. Dovrai inserirlo ogni volta che apri Readest. Non c'è modo di recuperare un PIN dimenticato — cancellare i dati dell'app è l'unico modo per reimpostarlo.",
|
||||
"Enter your current PIN, then choose a new 4-digit PIN.": "Inserisci il tuo PIN attuale, quindi scegli un nuovo PIN di 4 cifre.",
|
||||
"Enter your current PIN to disable the app lock.": "Inserisci il tuo PIN attuale per disattivare il blocco dell'app.",
|
||||
"PIN must be {{length}} digits": "Il PIN deve avere {{length}} cifre",
|
||||
"PINs do not match": "I PIN non corrispondono",
|
||||
"Current PIN": "PIN attuale",
|
||||
"New PIN": "Nuovo PIN",
|
||||
"Confirm new PIN": "Conferma nuovo PIN"
|
||||
}
|
||||
|
||||
@@ -1316,5 +1316,43 @@
|
||||
"Failed to download file: {{title}}": "ファイルのダウンロードに失敗しました:{{title}}",
|
||||
"Failed to delete cloud copy of the file: {{title}}": "ファイルのクラウドコピーの削除に失敗しました:{{title}}",
|
||||
"{{percentage}}% used": "{{percentage}}% 使用済み",
|
||||
"Resets in {{duration}}": "{{duration}}でリセット"
|
||||
"Resets in {{duration}}": "{{duration}}でリセット",
|
||||
"Set PIN…": "PINを設定…",
|
||||
"Require a 4-digit PIN to open Readest": "Readestを開くのに4桁のPINを要求する",
|
||||
"Change PIN…": "PINを変更…",
|
||||
"Disable PIN…": "PINを無効化…",
|
||||
"Sync passphrase ready": "同期パスフレーズの準備ができました",
|
||||
"This permanently deletes the encrypted credentials we sync (e.g., OPDS catalog passwords) on every device. Local copies are preserved. You will need to re-enter the sync passphrase or set a new one. Continue?": "これにより、各デバイスで同期している暗号化された資格情報(OPDS カタログのパスワードなど)が永続的に削除されます。ローカルのコピーは保持されます。同期パスフレーズを再入力するか、新しく設定する必要があります。続行しますか?",
|
||||
"Sync passphrase forgotten — all encrypted fields cleared": "同期パスフレーズを忘れました — 暗号化されたフィールドはすべてクリアされました",
|
||||
"Sync passphrase": "同期パスフレーズ",
|
||||
"Encrypts sensitive synced fields (like OPDS catalog credentials) before they leave your device. Set one now or wait — it will be requested when needed.": "機密性の高い同期フィールド(OPDS カタログの認証情報など)を、デバイスから出る前に暗号化します。今すぐ設定するか、待つこともできます — 必要なときに要求されます。",
|
||||
"Set on this account. Will be requested when needed to decrypt synced credentials.": "このアカウントで設定済みです。同期された資格情報を復号化する必要があるときに要求されます。",
|
||||
"Set passphrase": "パスフレーズを設定",
|
||||
"Unlock now": "今すぐロック解除",
|
||||
"Forgot passphrase": "パスフレーズを忘れました",
|
||||
"Incorrect PIN": "PINが正しくありません",
|
||||
"App locked": "アプリがロックされています",
|
||||
"Enter your PIN": "PINを入力してください",
|
||||
"Readest is locked. Enter your 4-digit PIN to continue.": "Readestはロックされています。続行するには4桁のPINを入力してください。",
|
||||
"PIN code": "PINコード",
|
||||
"Forgetting your PIN locks you out of this device. You'll need to clear the app's data to reset it.": "PINを忘れるとこのデバイスから締め出されます。リセットするにはアプリのデータを消去する必要があります。",
|
||||
"Passphrase must be at least 8 characters": "パスフレーズは 8 文字以上にしてください",
|
||||
"Passphrases do not match": "パスフレーズが一致しません",
|
||||
"Set sync passphrase": "同期パスフレーズを設定",
|
||||
"Enter sync passphrase": "同期パスフレーズを入力",
|
||||
"A sync passphrase encrypts your sensitive fields (like OPDS catalog credentials) before they sync. We never see this passphrase. Pick something memorable — there is no recovery without it.": "同期パスフレーズは、機密フィールド(OPDS カタログの認証情報など)を同期する前に暗号化します。このパスフレーズを当社が知ることはありません。覚えやすいものを選んでください — これがないと復元できません。",
|
||||
"Enter the sync passphrase you set on another device to decrypt your synced credentials.": "別のデバイスで設定した同期パスフレーズを入力して、同期された資格情報を復号化します。",
|
||||
"Confirm passphrase": "パスフレーズを確認",
|
||||
"Unlock": "ロック解除",
|
||||
"Set PIN": "PINを設定",
|
||||
"Change PIN": "PINを変更",
|
||||
"Disable PIN": "PINを無効化",
|
||||
"Pick a 4-digit PIN. You will need to enter it every time you open Readest. There is no way to recover a forgotten PIN — clearing the app data is the only way to reset it.": "4桁のPINを選んでください。Readestを開くたびに入力する必要があります。忘れたPINを復元する方法はありません — アプリのデータを消去することがリセットする唯一の方法です。",
|
||||
"Enter your current PIN, then choose a new 4-digit PIN.": "現在のPINを入力してから、新しい4桁のPINを選んでください。",
|
||||
"Enter your current PIN to disable the app lock.": "アプリのロックを解除するには現在のPINを入力してください。",
|
||||
"PIN must be {{length}} digits": "PINは{{length}}桁である必要があります",
|
||||
"PINs do not match": "PINが一致しません",
|
||||
"Current PIN": "現在のPIN",
|
||||
"New PIN": "新しいPIN",
|
||||
"Confirm new PIN": "新しいPINの確認"
|
||||
}
|
||||
|
||||
@@ -1316,5 +1316,43 @@
|
||||
"Failed to download file: {{title}}": "파일 다운로드 실패: {{title}}",
|
||||
"Failed to delete cloud copy of the file: {{title}}": "파일의 클라우드 사본 삭제에 실패했습니다: {{title}}",
|
||||
"{{percentage}}% used": "{{percentage}}% 사용됨",
|
||||
"Resets in {{duration}}": "{{duration}} 후 재설정"
|
||||
"Resets in {{duration}}": "{{duration}} 후 재설정",
|
||||
"Set PIN…": "PIN 설정…",
|
||||
"Require a 4-digit PIN to open Readest": "Readest를 열 때 4자리 PIN 요구",
|
||||
"Change PIN…": "PIN 변경…",
|
||||
"Disable PIN…": "PIN 비활성화…",
|
||||
"Sync passphrase ready": "동기화 암호 문구가 준비되었습니다",
|
||||
"This permanently deletes the encrypted credentials we sync (e.g., OPDS catalog passwords) on every device. Local copies are preserved. You will need to re-enter the sync passphrase or set a new one. Continue?": "이 작업을 수행하면 모든 기기에서 동기화하는 암호화된 자격 증명(예: OPDS 카탈로그 비밀번호)이 영구적으로 삭제됩니다. 로컬 복사본은 보존됩니다. 동기화 암호 문구를 다시 입력하거나 새로 설정해야 합니다. 계속하시겠습니까?",
|
||||
"Sync passphrase forgotten — all encrypted fields cleared": "동기화 암호 문구를 잊었습니다 — 모든 암호화된 필드가 지워졌습니다",
|
||||
"Sync passphrase": "동기화 암호 문구",
|
||||
"Encrypts sensitive synced fields (like OPDS catalog credentials) before they leave your device. Set one now or wait — it will be requested when needed.": "민감한 동기화 필드(예: OPDS 카탈로그 자격 증명)를 기기에서 나가기 전에 암호화합니다. 지금 설정하거나 나중에 — 필요할 때 요청됩니다.",
|
||||
"Set on this account. Will be requested when needed to decrypt synced credentials.": "이 계정에 설정되어 있습니다. 동기화된 자격 증명을 복호화해야 할 때 요청됩니다.",
|
||||
"Set passphrase": "암호 문구 설정",
|
||||
"Unlock now": "지금 잠금 해제",
|
||||
"Forgot passphrase": "암호 문구를 잊으셨나요",
|
||||
"Incorrect PIN": "잘못된 PIN",
|
||||
"App locked": "앱이 잠겼습니다",
|
||||
"Enter your PIN": "PIN을 입력하세요",
|
||||
"Readest is locked. Enter your 4-digit PIN to continue.": "Readest가 잠겼습니다. 계속하려면 4자리 PIN을 입력하세요.",
|
||||
"PIN code": "PIN 코드",
|
||||
"Forgetting your PIN locks you out of this device. You'll need to clear the app's data to reset it.": "PIN을 잊으면 이 기기에서 잠금이 걸립니다. 재설정하려면 앱 데이터를 지워야 합니다.",
|
||||
"Passphrase must be at least 8 characters": "암호 문구는 8자 이상이어야 합니다",
|
||||
"Passphrases do not match": "암호 문구가 일치하지 않습니다",
|
||||
"Set sync passphrase": "동기화 암호 문구 설정",
|
||||
"Enter sync passphrase": "동기화 암호 문구 입력",
|
||||
"A sync passphrase encrypts your sensitive fields (like OPDS catalog credentials) before they sync. We never see this passphrase. Pick something memorable — there is no recovery without it.": "동기화 암호 문구는 OPDS 카탈로그 자격 증명과 같은 민감한 필드를 동기화하기 전에 암호화합니다. 우리는 이 암호 문구를 결코 볼 수 없습니다. 기억하기 쉬운 것을 고르세요 — 잃어버리면 복구할 수 없습니다.",
|
||||
"Enter the sync passphrase you set on another device to decrypt your synced credentials.": "다른 기기에서 설정한 동기화 암호 문구를 입력하여 동기화된 자격 증명을 복호화합니다.",
|
||||
"Confirm passphrase": "암호 문구 확인",
|
||||
"Unlock": "잠금 해제",
|
||||
"Set PIN": "PIN 설정",
|
||||
"Change PIN": "PIN 변경",
|
||||
"Disable PIN": "PIN 비활성화",
|
||||
"Pick a 4-digit PIN. You will need to enter it every time you open Readest. There is no way to recover a forgotten PIN — clearing the app data is the only way to reset it.": "4자리 PIN을 선택하세요. Readest를 열 때마다 입력해야 합니다. 잊은 PIN을 복구할 방법은 없습니다 — 앱 데이터를 지우는 것이 재설정하는 유일한 방법입니다.",
|
||||
"Enter your current PIN, then choose a new 4-digit PIN.": "현재 PIN을 입력한 다음 새 4자리 PIN을 선택하세요.",
|
||||
"Enter your current PIN to disable the app lock.": "앱 잠금을 비활성화하려면 현재 PIN을 입력하세요.",
|
||||
"PIN must be {{length}} digits": "PIN은 {{length}}자리여야 합니다",
|
||||
"PINs do not match": "PIN이 일치하지 않습니다",
|
||||
"Current PIN": "현재 PIN",
|
||||
"New PIN": "새 PIN",
|
||||
"Confirm new PIN": "새 PIN 확인"
|
||||
}
|
||||
|
||||
@@ -1316,5 +1316,43 @@
|
||||
"Failed to download file: {{title}}": "Gagal memuat turun fail: {{title}}",
|
||||
"Failed to delete cloud copy of the file: {{title}}": "Gagal memadam salinan awan fail: {{title}}",
|
||||
"{{percentage}}% used": "{{percentage}}% telah digunakan",
|
||||
"Resets in {{duration}}": "Set semula dalam {{duration}}"
|
||||
"Resets in {{duration}}": "Set semula dalam {{duration}}",
|
||||
"Set PIN…": "Tetapkan PIN…",
|
||||
"Require a 4-digit PIN to open Readest": "Wajibkan PIN 4 digit untuk membuka Readest",
|
||||
"Change PIN…": "Tukar PIN…",
|
||||
"Disable PIN…": "Lumpuhkan PIN…",
|
||||
"Sync passphrase ready": "Frasa laluan penyegerakan sedia",
|
||||
"This permanently deletes the encrypted credentials we sync (e.g., OPDS catalog passwords) on every device. Local copies are preserved. You will need to re-enter the sync passphrase or set a new one. Continue?": "Ini memadamkan secara kekal bukti kelayakan tersulit yang kami selaraskan (cth., kata laluan katalog OPDS) pada setiap peranti. Salinan tempatan dikekalkan. Anda perlu memasukkan semula frasa laluan penyegerakan atau menetapkan yang baharu. Teruskan?",
|
||||
"Sync passphrase forgotten — all encrypted fields cleared": "Frasa laluan penyegerakan dilupakan — semua medan tersulit dikosongkan",
|
||||
"Sync passphrase": "Frasa laluan penyegerakan",
|
||||
"Encrypts sensitive synced fields (like OPDS catalog credentials) before they leave your device. Set one now or wait — it will be requested when needed.": "Mengenkripsi medan tersegerak sensitif (seperti bukti kelayakan katalog OPDS) sebelum ia meninggalkan peranti anda. Tetapkan satu sekarang atau tunggu — ia akan diminta apabila diperlukan.",
|
||||
"Set on this account. Will be requested when needed to decrypt synced credentials.": "Ditetapkan pada akaun ini. Akan diminta apabila diperlukan untuk menyahsulit bukti kelayakan yang disegerakkan.",
|
||||
"Set passphrase": "Tetapkan frasa laluan",
|
||||
"Unlock now": "Buka kunci sekarang",
|
||||
"Forgot passphrase": "Lupa frasa laluan",
|
||||
"Incorrect PIN": "PIN salah",
|
||||
"App locked": "Aplikasi terkunci",
|
||||
"Enter your PIN": "Masukkan PIN anda",
|
||||
"Readest is locked. Enter your 4-digit PIN to continue.": "Readest terkunci. Masukkan PIN 4 digit anda untuk meneruskan.",
|
||||
"PIN code": "Kod PIN",
|
||||
"Forgetting your PIN locks you out of this device. You'll need to clear the app's data to reset it.": "Terlupa PIN akan mengunci anda daripada peranti ini. Anda perlu mengosongkan data aplikasi untuk menetapkannya semula.",
|
||||
"Passphrase must be at least 8 characters": "Frasa laluan mestilah sekurang-kurangnya 8 aksara",
|
||||
"Passphrases do not match": "Frasa laluan tidak sepadan",
|
||||
"Set sync passphrase": "Tetapkan frasa laluan penyegerakan",
|
||||
"Enter sync passphrase": "Masukkan frasa laluan penyegerakan",
|
||||
"A sync passphrase encrypts your sensitive fields (like OPDS catalog credentials) before they sync. We never see this passphrase. Pick something memorable — there is no recovery without it.": "Frasa laluan penyegerakan menyulitkan medan sensitif anda (seperti bukti kelayakan katalog OPDS) sebelum ia diselaraskan. Kami tidak pernah melihat frasa laluan ini. Pilih sesuatu yang mudah diingati — tiada pemulihan tanpanya.",
|
||||
"Enter the sync passphrase you set on another device to decrypt your synced credentials.": "Masukkan frasa laluan penyegerakan yang anda tetapkan pada peranti lain untuk menyahsulit bukti kelayakan yang disegerakkan.",
|
||||
"Confirm passphrase": "Sahkan frasa laluan",
|
||||
"Unlock": "Buka kunci",
|
||||
"Set PIN": "Tetapkan PIN",
|
||||
"Change PIN": "Tukar PIN",
|
||||
"Disable PIN": "Lumpuhkan PIN",
|
||||
"Pick a 4-digit PIN. You will need to enter it every time you open Readest. There is no way to recover a forgotten PIN — clearing the app data is the only way to reset it.": "Pilih PIN 4 digit. Anda perlu memasukkannya setiap kali anda membuka Readest. Tiada cara untuk memulihkan PIN yang terlupa — mengosongkan data aplikasi adalah satu-satunya cara untuk menetapkannya semula.",
|
||||
"Enter your current PIN, then choose a new 4-digit PIN.": "Masukkan PIN semasa anda, kemudian pilih PIN 4 digit baharu.",
|
||||
"Enter your current PIN to disable the app lock.": "Masukkan PIN semasa anda untuk melumpuhkan kunci aplikasi.",
|
||||
"PIN must be {{length}} digits": "PIN mesti mengandungi {{length}} digit",
|
||||
"PINs do not match": "PIN tidak sepadan",
|
||||
"Current PIN": "PIN semasa",
|
||||
"New PIN": "PIN baharu",
|
||||
"Confirm new PIN": "Sahkan PIN baharu"
|
||||
}
|
||||
|
||||
@@ -1340,5 +1340,43 @@
|
||||
"Failed to download file: {{title}}": "Downloaden van bestand mislukt: {{title}}",
|
||||
"Failed to delete cloud copy of the file: {{title}}": "Verwijderen van cloudkopie van het bestand is mislukt: {{title}}",
|
||||
"{{percentage}}% used": "{{percentage}}% gebruikt",
|
||||
"Resets in {{duration}}": "Reset over {{duration}}"
|
||||
"Resets in {{duration}}": "Reset over {{duration}}",
|
||||
"Set PIN…": "PIN instellen…",
|
||||
"Require a 4-digit PIN to open Readest": "4-cijferige PIN vereisen om Readest te openen",
|
||||
"Change PIN…": "PIN wijzigen…",
|
||||
"Disable PIN…": "PIN uitschakelen…",
|
||||
"Sync passphrase ready": "Sync-wachtwoordzin gereed",
|
||||
"This permanently deletes the encrypted credentials we sync (e.g., OPDS catalog passwords) on every device. Local copies are preserved. You will need to re-enter the sync passphrase or set a new one. Continue?": "Dit verwijdert permanent de versleutelde inloggegevens die we synchroniseren (bijv. OPDS-catalogi-wachtwoorden) op elk apparaat. Lokale kopieën blijven behouden. Je moet de sync-wachtwoordzin opnieuw invoeren of een nieuwe instellen. Doorgaan?",
|
||||
"Sync passphrase forgotten — all encrypted fields cleared": "Sync-wachtwoordzin vergeten — alle versleutelde velden gewist",
|
||||
"Sync passphrase": "Sync-wachtwoordzin",
|
||||
"Encrypts sensitive synced fields (like OPDS catalog credentials) before they leave your device. Set one now or wait — it will be requested when needed.": "Versleutelt gevoelige gesynchroniseerde velden (zoals OPDS-cataloguscredentials) voordat ze je apparaat verlaten. Stel er nu een in of wacht — er wordt om gevraagd wanneer nodig.",
|
||||
"Set on this account. Will be requested when needed to decrypt synced credentials.": "Ingesteld voor dit account. Wordt opgevraagd wanneer nodig om gesynchroniseerde inloggegevens te ontsleutelen.",
|
||||
"Set passphrase": "Wachtwoordzin instellen",
|
||||
"Unlock now": "Nu ontgrendelen",
|
||||
"Forgot passphrase": "Wachtwoordzin vergeten",
|
||||
"Incorrect PIN": "Onjuiste PIN",
|
||||
"App locked": "App vergrendeld",
|
||||
"Enter your PIN": "Voer uw PIN in",
|
||||
"Readest is locked. Enter your 4-digit PIN to continue.": "Readest is vergrendeld. Voer uw 4-cijferige PIN in om door te gaan.",
|
||||
"PIN code": "PIN-code",
|
||||
"Forgetting your PIN locks you out of this device. You'll need to clear the app's data to reset it.": "Als u uw PIN vergeet, wordt u buitengesloten van dit apparaat. U moet de app-gegevens wissen om hem opnieuw in te stellen.",
|
||||
"Passphrase must be at least 8 characters": "De wachtwoordzin moet minstens 8 tekens lang zijn",
|
||||
"Passphrases do not match": "Wachtwoordzinnen komen niet overeen",
|
||||
"Set sync passphrase": "Sync-wachtwoordzin instellen",
|
||||
"Enter sync passphrase": "Sync-wachtwoordzin invoeren",
|
||||
"A sync passphrase encrypts your sensitive fields (like OPDS catalog credentials) before they sync. We never see this passphrase. Pick something memorable — there is no recovery without it.": "Een sync-wachtwoordzin versleutelt je gevoelige velden (zoals OPDS-cataloguscredentials) voordat ze worden gesynchroniseerd. We zien deze wachtwoordzin nooit. Kies iets dat je goed kunt onthouden — zonder de zin is er geen herstel mogelijk.",
|
||||
"Enter the sync passphrase you set on another device to decrypt your synced credentials.": "Voer de sync-wachtwoordzin in die je op een ander apparaat hebt ingesteld om je gesynchroniseerde inloggegevens te ontsleutelen.",
|
||||
"Confirm passphrase": "Wachtwoordzin bevestigen",
|
||||
"Unlock": "Ontgrendelen",
|
||||
"Set PIN": "PIN instellen",
|
||||
"Change PIN": "PIN wijzigen",
|
||||
"Disable PIN": "PIN uitschakelen",
|
||||
"Pick a 4-digit PIN. You will need to enter it every time you open Readest. There is no way to recover a forgotten PIN — clearing the app data is the only way to reset it.": "Kies een 4-cijferige PIN. U moet hem invoeren elke keer dat u Readest opent. Er is geen manier om een vergeten PIN te herstellen — de app-gegevens wissen is de enige manier om hem opnieuw in te stellen.",
|
||||
"Enter your current PIN, then choose a new 4-digit PIN.": "Voer uw huidige PIN in en kies vervolgens een nieuwe 4-cijferige PIN.",
|
||||
"Enter your current PIN to disable the app lock.": "Voer uw huidige PIN in om de app-vergrendeling uit te schakelen.",
|
||||
"PIN must be {{length}} digits": "PIN moet uit {{length}} cijfers bestaan",
|
||||
"PINs do not match": "PINs komen niet overeen",
|
||||
"Current PIN": "Huidige PIN",
|
||||
"New PIN": "Nieuwe PIN",
|
||||
"Confirm new PIN": "Nieuwe PIN bevestigen"
|
||||
}
|
||||
|
||||
@@ -1388,5 +1388,43 @@
|
||||
"Failed to download file: {{title}}": "Nie udało się pobrać pliku: {{title}}",
|
||||
"Failed to delete cloud copy of the file: {{title}}": "Nie udało się usunąć kopii pliku w chmurze: {{title}}",
|
||||
"{{percentage}}% used": "Wykorzystano {{percentage}}%",
|
||||
"Resets in {{duration}}": "Resetuje się za {{duration}}"
|
||||
"Resets in {{duration}}": "Resetuje się za {{duration}}",
|
||||
"Set PIN…": "Ustaw PIN…",
|
||||
"Require a 4-digit PIN to open Readest": "Wymagaj 4-cyfrowego PIN-u do otwarcia Readest",
|
||||
"Change PIN…": "Zmień PIN…",
|
||||
"Disable PIN…": "Wyłącz PIN…",
|
||||
"Sync passphrase ready": "Hasło synchronizacji gotowe",
|
||||
"This permanently deletes the encrypted credentials we sync (e.g., OPDS catalog passwords) on every device. Local copies are preserved. You will need to re-enter the sync passphrase or set a new one. Continue?": "Spowoduje to trwałe usunięcie zaszyfrowanych danych uwierzytelniających, które synchronizujemy (np. haseł katalogu OPDS), na każdym urządzeniu. Lokalne kopie zostaną zachowane. Konieczne będzie ponowne wprowadzenie hasła synchronizacji lub ustawienie nowego. Kontynuować?",
|
||||
"Sync passphrase forgotten — all encrypted fields cleared": "Zapomniano hasła synchronizacji — wszystkie zaszyfrowane pola wyczyszczone",
|
||||
"Sync passphrase": "Hasło synchronizacji",
|
||||
"Encrypts sensitive synced fields (like OPDS catalog credentials) before they leave your device. Set one now or wait — it will be requested when needed.": "Szyfruje wrażliwe synchronizowane pola (np. dane uwierzytelniające katalogu OPDS) przed opuszczeniem Twojego urządzenia. Ustaw je teraz lub poczekaj — zostanie poproszone, gdy będzie potrzebne.",
|
||||
"Set on this account. Will be requested when needed to decrypt synced credentials.": "Ustawione na tym koncie. Zostanie poproszone, gdy będzie potrzebne do odszyfrowania zsynchronizowanych danych uwierzytelniających.",
|
||||
"Set passphrase": "Ustaw hasło",
|
||||
"Unlock now": "Odblokuj teraz",
|
||||
"Forgot passphrase": "Zapomniałem hasła",
|
||||
"Incorrect PIN": "Nieprawidłowy PIN",
|
||||
"App locked": "Aplikacja zablokowana",
|
||||
"Enter your PIN": "Wprowadź swój PIN",
|
||||
"Readest is locked. Enter your 4-digit PIN to continue.": "Readest jest zablokowany. Wprowadź swój 4-cyfrowy PIN, aby kontynuować.",
|
||||
"PIN code": "Kod PIN",
|
||||
"Forgetting your PIN locks you out of this device. You'll need to clear the app's data to reset it.": "Zapomnienie PIN-u zablokuje Cię na tym urządzeniu. Aby go zresetować, musisz wyczyścić dane aplikacji.",
|
||||
"Passphrase must be at least 8 characters": "Hasło musi mieć co najmniej 8 znaków",
|
||||
"Passphrases do not match": "Hasła nie pasują do siebie",
|
||||
"Set sync passphrase": "Ustaw hasło synchronizacji",
|
||||
"Enter sync passphrase": "Wprowadź hasło synchronizacji",
|
||||
"A sync passphrase encrypts your sensitive fields (like OPDS catalog credentials) before they sync. We never see this passphrase. Pick something memorable — there is no recovery without it.": "Hasło synchronizacji szyfruje Twoje wrażliwe pola (np. dane uwierzytelniające katalogu OPDS) przed synchronizacją. Nigdy nie widzimy tego hasła. Wybierz coś łatwego do zapamiętania — bez niego nie ma odzyskiwania.",
|
||||
"Enter the sync passphrase you set on another device to decrypt your synced credentials.": "Wprowadź hasło synchronizacji ustawione na innym urządzeniu, aby odszyfrować zsynchronizowane dane uwierzytelniające.",
|
||||
"Confirm passphrase": "Potwierdź hasło",
|
||||
"Unlock": "Odblokuj",
|
||||
"Set PIN": "Ustaw PIN",
|
||||
"Change PIN": "Zmień PIN",
|
||||
"Disable PIN": "Wyłącz PIN",
|
||||
"Pick a 4-digit PIN. You will need to enter it every time you open Readest. There is no way to recover a forgotten PIN — clearing the app data is the only way to reset it.": "Wybierz 4-cyfrowy PIN. Będziesz musiał go wprowadzać za każdym razem, gdy otworzysz Readest. Nie ma sposobu na odzyskanie zapomnianego PIN-u — wyczyszczenie danych aplikacji to jedyny sposób na jego zresetowanie.",
|
||||
"Enter your current PIN, then choose a new 4-digit PIN.": "Wprowadź swój obecny PIN, a następnie wybierz nowy 4-cyfrowy PIN.",
|
||||
"Enter your current PIN to disable the app lock.": "Wprowadź swój obecny PIN, aby wyłączyć blokadę aplikacji.",
|
||||
"PIN must be {{length}} digits": "PIN musi mieć {{length}} cyfr",
|
||||
"PINs do not match": "PIN-y nie pasują do siebie",
|
||||
"Current PIN": "Obecny PIN",
|
||||
"New PIN": "Nowy PIN",
|
||||
"Confirm new PIN": "Potwierdź nowy PIN"
|
||||
}
|
||||
|
||||
@@ -1364,5 +1364,43 @@
|
||||
"Failed to download file: {{title}}": "Falha ao baixar arquivo: {{title}}",
|
||||
"Failed to delete cloud copy of the file: {{title}}": "Falha ao excluir cópia na nuvem do arquivo: {{title}}",
|
||||
"{{percentage}}% used": "{{percentage}}% utilizado",
|
||||
"Resets in {{duration}}": "Redefine em {{duration}}"
|
||||
"Resets in {{duration}}": "Redefine em {{duration}}",
|
||||
"Set PIN…": "Definir PIN…",
|
||||
"Require a 4-digit PIN to open Readest": "Exigir um PIN de 4 dígitos para abrir o Readest",
|
||||
"Change PIN…": "Alterar PIN…",
|
||||
"Disable PIN…": "Desativar PIN…",
|
||||
"Sync passphrase ready": "Frase secreta de sincronização pronta",
|
||||
"This permanently deletes the encrypted credentials we sync (e.g., OPDS catalog passwords) on every device. Local copies are preserved. You will need to re-enter the sync passphrase or set a new one. Continue?": "Isso exclui permanentemente as credenciais criptografadas que sincronizamos (por exemplo, senhas do catálogo OPDS) em todos os dispositivos. As cópias locais são preservadas. Você precisará digitar novamente a frase secreta de sincronização ou definir uma nova. Continuar?",
|
||||
"Sync passphrase forgotten — all encrypted fields cleared": "Frase secreta de sincronização esquecida — todos os campos criptografados foram limpos",
|
||||
"Sync passphrase": "Frase secreta de sincronização",
|
||||
"Encrypts sensitive synced fields (like OPDS catalog credentials) before they leave your device. Set one now or wait — it will be requested when needed.": "Criptografa campos sincronizados sensíveis (como credenciais de catálogo OPDS) antes que saiam do seu dispositivo. Defina uma agora ou aguarde — será solicitada quando necessário.",
|
||||
"Set on this account. Will be requested when needed to decrypt synced credentials.": "Definida nesta conta. Será solicitada quando necessária para descriptografar credenciais sincronizadas.",
|
||||
"Set passphrase": "Definir frase secreta",
|
||||
"Unlock now": "Desbloquear agora",
|
||||
"Forgot passphrase": "Esqueci a frase secreta",
|
||||
"Incorrect PIN": "PIN incorreto",
|
||||
"App locked": "Aplicativo bloqueado",
|
||||
"Enter your PIN": "Digite seu PIN",
|
||||
"Readest is locked. Enter your 4-digit PIN to continue.": "O Readest está bloqueado. Digite seu PIN de 4 dígitos para continuar.",
|
||||
"PIN code": "Código PIN",
|
||||
"Forgetting your PIN locks you out of this device. You'll need to clear the app's data to reset it.": "Esquecer o PIN bloqueia você neste dispositivo. Você precisará limpar os dados do aplicativo para redefini-lo.",
|
||||
"Passphrase must be at least 8 characters": "A frase secreta deve ter pelo menos 8 caracteres",
|
||||
"Passphrases do not match": "As frases secretas não coincidem",
|
||||
"Set sync passphrase": "Definir frase secreta de sincronização",
|
||||
"Enter sync passphrase": "Digite a frase secreta de sincronização",
|
||||
"A sync passphrase encrypts your sensitive fields (like OPDS catalog credentials) before they sync. We never see this passphrase. Pick something memorable — there is no recovery without it.": "Uma frase secreta de sincronização criptografa seus campos sensíveis (como credenciais do catálogo OPDS) antes que sejam sincronizados. Nunca vemos essa frase secreta. Escolha algo memorável — não há recuperação sem ela.",
|
||||
"Enter the sync passphrase you set on another device to decrypt your synced credentials.": "Digite a frase secreta de sincronização que você definiu em outro dispositivo para descriptografar suas credenciais sincronizadas.",
|
||||
"Confirm passphrase": "Confirmar frase secreta",
|
||||
"Unlock": "Desbloquear",
|
||||
"Set PIN": "Definir PIN",
|
||||
"Change PIN": "Alterar PIN",
|
||||
"Disable PIN": "Desativar PIN",
|
||||
"Pick a 4-digit PIN. You will need to enter it every time you open Readest. There is no way to recover a forgotten PIN — clearing the app data is the only way to reset it.": "Escolha um PIN de 4 dígitos. Você precisará digitá-lo toda vez que abrir o Readest. Não há como recuperar um PIN esquecido — limpar os dados do aplicativo é a única maneira de redefini-lo.",
|
||||
"Enter your current PIN, then choose a new 4-digit PIN.": "Digite seu PIN atual e, em seguida, escolha um novo PIN de 4 dígitos.",
|
||||
"Enter your current PIN to disable the app lock.": "Digite seu PIN atual para desativar o bloqueio do aplicativo.",
|
||||
"PIN must be {{length}} digits": "O PIN deve ter {{length}} dígitos",
|
||||
"PINs do not match": "Os PINs não coincidem",
|
||||
"Current PIN": "PIN atual",
|
||||
"New PIN": "Novo PIN",
|
||||
"Confirm new PIN": "Confirmar novo PIN"
|
||||
}
|
||||
|
||||
@@ -1364,5 +1364,43 @@
|
||||
"Failed to download file: {{title}}": "Falha ao baixar arquivo: {{title}}",
|
||||
"Failed to delete cloud copy of the file: {{title}}": "Falha ao excluir cópia na nuvem do arquivo: {{title}}",
|
||||
"{{percentage}}% used": "{{percentage}}% utilizado",
|
||||
"Resets in {{duration}}": "Repõe em {{duration}}"
|
||||
"Resets in {{duration}}": "Repõe em {{duration}}",
|
||||
"Set PIN…": "Definir PIN…",
|
||||
"Require a 4-digit PIN to open Readest": "Exigir um PIN de 4 dígitos para abrir o Readest",
|
||||
"Change PIN…": "Alterar PIN…",
|
||||
"Disable PIN…": "Desativar PIN…",
|
||||
"Sync passphrase ready": "Frase-passe de sincronização pronta",
|
||||
"This permanently deletes the encrypted credentials we sync (e.g., OPDS catalog passwords) on every device. Local copies are preserved. You will need to re-enter the sync passphrase or set a new one. Continue?": "Isto elimina permanentemente as credenciais encriptadas que sincronizamos (por exemplo, palavras-passe do catálogo OPDS) em todos os dispositivos. As cópias locais são preservadas. Terá de voltar a introduzir a frase-passe de sincronização ou definir uma nova. Continuar?",
|
||||
"Sync passphrase forgotten — all encrypted fields cleared": "Frase-passe de sincronização esquecida — todos os campos encriptados foram apagados",
|
||||
"Sync passphrase": "Frase-passe de sincronização",
|
||||
"Encrypts sensitive synced fields (like OPDS catalog credentials) before they leave your device. Set one now or wait — it will be requested when needed.": "Encripta campos sincronizados sensíveis (como credenciais do catálogo OPDS) antes de saírem do seu dispositivo. Defina uma agora ou aguarde — será solicitada quando necessário.",
|
||||
"Set on this account. Will be requested when needed to decrypt synced credentials.": "Definida nesta conta. Será solicitada quando for necessária para desencriptar credenciais sincronizadas.",
|
||||
"Set passphrase": "Definir frase-passe",
|
||||
"Unlock now": "Desbloquear agora",
|
||||
"Forgot passphrase": "Esqueci a frase-passe",
|
||||
"Incorrect PIN": "PIN incorreto",
|
||||
"App locked": "Aplicação bloqueada",
|
||||
"Enter your PIN": "Introduza o seu PIN",
|
||||
"Readest is locked. Enter your 4-digit PIN to continue.": "O Readest está bloqueado. Introduza o seu PIN de 4 dígitos para continuar.",
|
||||
"PIN code": "Código PIN",
|
||||
"Forgetting your PIN locks you out of this device. You'll need to clear the app's data to reset it.": "Esquecer o PIN bloqueia-o neste dispositivo. Terá de limpar os dados da aplicação para o repor.",
|
||||
"Passphrase must be at least 8 characters": "A frase-passe deve ter pelo menos 8 caracteres",
|
||||
"Passphrases do not match": "As frases-passe não coincidem",
|
||||
"Set sync passphrase": "Definir frase-passe de sincronização",
|
||||
"Enter sync passphrase": "Introduza a frase-passe de sincronização",
|
||||
"A sync passphrase encrypts your sensitive fields (like OPDS catalog credentials) before they sync. We never see this passphrase. Pick something memorable — there is no recovery without it.": "Uma frase-passe de sincronização encripta os seus campos sensíveis (como credenciais do catálogo OPDS) antes de serem sincronizados. Nunca vemos esta frase-passe. Escolha algo memorável — não há recuperação sem ela.",
|
||||
"Enter the sync passphrase you set on another device to decrypt your synced credentials.": "Introduza a frase-passe de sincronização que definiu noutro dispositivo para desencriptar as suas credenciais sincronizadas.",
|
||||
"Confirm passphrase": "Confirmar frase-passe",
|
||||
"Unlock": "Desbloquear",
|
||||
"Set PIN": "Definir PIN",
|
||||
"Change PIN": "Alterar PIN",
|
||||
"Disable PIN": "Desativar PIN",
|
||||
"Pick a 4-digit PIN. You will need to enter it every time you open Readest. There is no way to recover a forgotten PIN — clearing the app data is the only way to reset it.": "Escolha um PIN de 4 dígitos. Terá de o introduzir sempre que abrir o Readest. Não há forma de recuperar um PIN esquecido — limpar os dados da aplicação é a única forma de o repor.",
|
||||
"Enter your current PIN, then choose a new 4-digit PIN.": "Introduza o seu PIN atual e, em seguida, escolha um novo PIN de 4 dígitos.",
|
||||
"Enter your current PIN to disable the app lock.": "Introduza o seu PIN atual para desativar o bloqueio da aplicação.",
|
||||
"PIN must be {{length}} digits": "O PIN deve ter {{length}} dígitos",
|
||||
"PINs do not match": "Os PIN não coincidem",
|
||||
"Current PIN": "PIN atual",
|
||||
"New PIN": "Novo PIN",
|
||||
"Confirm new PIN": "Confirmar novo PIN"
|
||||
}
|
||||
|
||||
@@ -1364,5 +1364,43 @@
|
||||
"Failed to download file: {{title}}": "Nu s-a putut descărca fișierul: {{title}}",
|
||||
"Failed to delete cloud copy of the file: {{title}}": "Nu s-a putut șterge copia din cloud a fișierului: {{title}}",
|
||||
"{{percentage}}% used": "{{percentage}}% utilizat",
|
||||
"Resets in {{duration}}": "Se resetează în {{duration}}"
|
||||
"Resets in {{duration}}": "Se resetează în {{duration}}",
|
||||
"Set PIN…": "Setează PIN…",
|
||||
"Require a 4-digit PIN to open Readest": "Solicită un PIN de 4 cifre pentru a deschide Readest",
|
||||
"Change PIN…": "Schimbă PIN…",
|
||||
"Disable PIN…": "Dezactivează PIN…",
|
||||
"Sync passphrase ready": "Fraza de acces pentru sincronizare este pregătită",
|
||||
"This permanently deletes the encrypted credentials we sync (e.g., OPDS catalog passwords) on every device. Local copies are preserved. You will need to re-enter the sync passphrase or set a new one. Continue?": "Această acțiune șterge definitiv datele de autentificare criptate pe care le sincronizăm (de exemplu, parolele catalogului OPDS) pe fiecare dispozitiv. Copiile locale sunt păstrate. Va trebui să reintroduci fraza de acces pentru sincronizare sau să setezi una nouă. Continui?",
|
||||
"Sync passphrase forgotten — all encrypted fields cleared": "Fraza de acces pentru sincronizare a fost uitată — toate câmpurile criptate au fost șterse",
|
||||
"Sync passphrase": "Frază de acces pentru sincronizare",
|
||||
"Encrypts sensitive synced fields (like OPDS catalog credentials) before they leave your device. Set one now or wait — it will be requested when needed.": "Criptează câmpurile sensibile sincronizate (cum ar fi datele de autentificare ale catalogului OPDS) înainte ca acestea să părăsească dispozitivul tău. Setează una acum sau așteaptă — va fi solicitată când va fi nevoie.",
|
||||
"Set on this account. Will be requested when needed to decrypt synced credentials.": "Setată pe acest cont. Va fi solicitată atunci când va fi necesară pentru a decripta datele de autentificare sincronizate.",
|
||||
"Set passphrase": "Setează fraza de acces",
|
||||
"Unlock now": "Deblochează acum",
|
||||
"Forgot passphrase": "Am uitat fraza de acces",
|
||||
"Incorrect PIN": "PIN incorect",
|
||||
"App locked": "Aplicația este blocată",
|
||||
"Enter your PIN": "Introdu PIN-ul",
|
||||
"Readest is locked. Enter your 4-digit PIN to continue.": "Readest este blocat. Introdu PIN-ul de 4 cifre pentru a continua.",
|
||||
"PIN code": "Cod PIN",
|
||||
"Forgetting your PIN locks you out of this device. You'll need to clear the app's data to reset it.": "Dacă uiți PIN-ul, vei fi blocat pe acest dispozitiv. Va trebui să ștergi datele aplicației pentru a-l reseta.",
|
||||
"Passphrase must be at least 8 characters": "Fraza de acces trebuie să aibă cel puțin 8 caractere",
|
||||
"Passphrases do not match": "Frazele de acces nu se potrivesc",
|
||||
"Set sync passphrase": "Setează fraza de acces pentru sincronizare",
|
||||
"Enter sync passphrase": "Introdu fraza de acces pentru sincronizare",
|
||||
"A sync passphrase encrypts your sensitive fields (like OPDS catalog credentials) before they sync. We never see this passphrase. Pick something memorable — there is no recovery without it.": "O frază de acces pentru sincronizare îți criptează câmpurile sensibile (cum ar fi datele de autentificare ale catalogului OPDS) înainte de sincronizare. Nu vedem niciodată această frază. Alege ceva ușor de reținut — nu există recuperare fără ea.",
|
||||
"Enter the sync passphrase you set on another device to decrypt your synced credentials.": "Introdu fraza de acces pentru sincronizare pe care ai setat-o pe un alt dispozitiv pentru a-ți decripta datele de autentificare sincronizate.",
|
||||
"Confirm passphrase": "Confirmă fraza de acces",
|
||||
"Unlock": "Deblochează",
|
||||
"Set PIN": "Setează PIN",
|
||||
"Change PIN": "Schimbă PIN",
|
||||
"Disable PIN": "Dezactivează PIN",
|
||||
"Pick a 4-digit PIN. You will need to enter it every time you open Readest. There is no way to recover a forgotten PIN — clearing the app data is the only way to reset it.": "Alege un PIN de 4 cifre. Va trebui să îl introduci de fiecare dată când deschizi Readest. Nu există nicio modalitate de a recupera un PIN uitat — ștergerea datelor aplicației este singura modalitate de a-l reseta.",
|
||||
"Enter your current PIN, then choose a new 4-digit PIN.": "Introdu PIN-ul actual, apoi alege un nou PIN de 4 cifre.",
|
||||
"Enter your current PIN to disable the app lock.": "Introdu PIN-ul actual pentru a dezactiva blocarea aplicației.",
|
||||
"PIN must be {{length}} digits": "PIN-ul trebuie să aibă {{length}} cifre",
|
||||
"PINs do not match": "PIN-urile nu se potrivesc",
|
||||
"Current PIN": "PIN actual",
|
||||
"New PIN": "PIN nou",
|
||||
"Confirm new PIN": "Confirmă PIN-ul nou"
|
||||
}
|
||||
|
||||
@@ -1388,5 +1388,43 @@
|
||||
"Failed to download file: {{title}}": "Не удалось скачать файл: {{title}}",
|
||||
"Failed to delete cloud copy of the file: {{title}}": "Не удалось удалить копию файла в облаке: {{title}}",
|
||||
"{{percentage}}% used": "Использовано {{percentage}}%",
|
||||
"Resets in {{duration}}": "Сброс через {{duration}}"
|
||||
"Resets in {{duration}}": "Сброс через {{duration}}",
|
||||
"Set PIN…": "Установить PIN…",
|
||||
"Require a 4-digit PIN to open Readest": "Запрашивать 4-значный PIN при открытии Readest",
|
||||
"Change PIN…": "Изменить PIN…",
|
||||
"Disable PIN…": "Отключить PIN…",
|
||||
"Sync passphrase ready": "Секретная фраза синхронизации готова",
|
||||
"This permanently deletes the encrypted credentials we sync (e.g., OPDS catalog passwords) on every device. Local copies are preserved. You will need to re-enter the sync passphrase or set a new one. Continue?": "Это окончательно удалит зашифрованные учётные данные, которые мы синхронизируем (например, пароли каталога OPDS), на каждом устройстве. Локальные копии сохраняются. Вам потребуется ввести секретную фразу синхронизации заново или задать новую. Продолжить?",
|
||||
"Sync passphrase forgotten — all encrypted fields cleared": "Секретная фраза синхронизации забыта — все зашифрованные поля очищены",
|
||||
"Sync passphrase": "Секретная фраза синхронизации",
|
||||
"Encrypts sensitive synced fields (like OPDS catalog credentials) before they leave your device. Set one now or wait — it will be requested when needed.": "Шифрует конфиденциальные синхронизируемые поля (например, учётные данные каталога OPDS) перед тем, как они покинут ваше устройство. Задайте её сейчас или позже — она будет запрошена при необходимости.",
|
||||
"Set on this account. Will be requested when needed to decrypt synced credentials.": "Задана для этого аккаунта. Будет запрошена при необходимости для расшифровки синхронизированных учётных данных.",
|
||||
"Set passphrase": "Задать секретную фразу",
|
||||
"Unlock now": "Разблокировать сейчас",
|
||||
"Forgot passphrase": "Забыли секретную фразу",
|
||||
"Incorrect PIN": "Неверный PIN",
|
||||
"App locked": "Приложение заблокировано",
|
||||
"Enter your PIN": "Введите PIN",
|
||||
"Readest is locked. Enter your 4-digit PIN to continue.": "Readest заблокирован. Введите 4-значный PIN, чтобы продолжить.",
|
||||
"PIN code": "PIN-код",
|
||||
"Forgetting your PIN locks you out of this device. You'll need to clear the app's data to reset it.": "Если вы забудете PIN, вы потеряете доступ к этому устройству. Чтобы сбросить его, потребуется очистить данные приложения.",
|
||||
"Passphrase must be at least 8 characters": "Секретная фраза должна содержать не менее 8 символов",
|
||||
"Passphrases do not match": "Секретные фразы не совпадают",
|
||||
"Set sync passphrase": "Задать секретную фразу синхронизации",
|
||||
"Enter sync passphrase": "Введите секретную фразу синхронизации",
|
||||
"A sync passphrase encrypts your sensitive fields (like OPDS catalog credentials) before they sync. We never see this passphrase. Pick something memorable — there is no recovery without it.": "Секретная фраза синхронизации шифрует ваши конфиденциальные поля (например, учётные данные каталога OPDS) перед синхронизацией. Мы никогда не видим эту фразу. Выберите что-то запоминающееся — без неё восстановление невозможно.",
|
||||
"Enter the sync passphrase you set on another device to decrypt your synced credentials.": "Введите секретную фразу синхронизации, которую вы задали на другом устройстве, чтобы расшифровать синхронизированные учётные данные.",
|
||||
"Confirm passphrase": "Подтвердите секретную фразу",
|
||||
"Unlock": "Разблокировать",
|
||||
"Set PIN": "Установить PIN",
|
||||
"Change PIN": "Изменить PIN",
|
||||
"Disable PIN": "Отключить PIN",
|
||||
"Pick a 4-digit PIN. You will need to enter it every time you open Readest. There is no way to recover a forgotten PIN — clearing the app data is the only way to reset it.": "Выберите 4-значный PIN. Его нужно будет вводить каждый раз при открытии Readest. Восстановить забытый PIN невозможно — очистка данных приложения — единственный способ сбросить его.",
|
||||
"Enter your current PIN, then choose a new 4-digit PIN.": "Введите текущий PIN, а затем выберите новый 4-значный PIN.",
|
||||
"Enter your current PIN to disable the app lock.": "Введите текущий PIN, чтобы отключить блокировку приложения.",
|
||||
"PIN must be {{length}} digits": "PIN должен состоять из {{length}} цифр",
|
||||
"PINs do not match": "PIN-коды не совпадают",
|
||||
"Current PIN": "Текущий PIN",
|
||||
"New PIN": "Новый PIN",
|
||||
"Confirm new PIN": "Подтвердить новый PIN"
|
||||
}
|
||||
|
||||
@@ -1340,5 +1340,43 @@
|
||||
"Failed to download file: {{title}}": "ගොනුව බාගැනීමට අසමත්: {{title}}",
|
||||
"Failed to delete cloud copy of the file: {{title}}": "ගොනුවේ cloud පිටපත මකා දැමීමට අසමත්: {{title}}",
|
||||
"{{percentage}}% used": "{{percentage}}% භාවිතයි",
|
||||
"Resets in {{duration}}": "{{duration}}කින් යළි පිහිටුවයි"
|
||||
"Resets in {{duration}}": "{{duration}}කින් යළි පිහිටුවයි",
|
||||
"Set PIN…": "PIN සකසන්න…",
|
||||
"Require a 4-digit PIN to open Readest": "Readest විවෘත කිරීමට ඉලක්කම් 4ක PIN එකක් ඉල්ලන්න",
|
||||
"Change PIN…": "PIN වෙනස් කරන්න…",
|
||||
"Disable PIN…": "PIN අක්රිය කරන්න…",
|
||||
"Sync passphrase ready": "සමමුහුර්ත මුරපදය සූදානම්",
|
||||
"This permanently deletes the encrypted credentials we sync (e.g., OPDS catalog passwords) on every device. Local copies are preserved. You will need to re-enter the sync passphrase or set a new one. Continue?": "මෙය සෑම උපාංගයකම අප සමමුහුර්ත කරන සංකේතාංකිත අක්තපත්ර (උදා., OPDS නාමාවලි මුරපද) ස්ථිරවම මකා දමයි. දේශීය පිටපත් රැකේ. ඔබට සමමුහුර්ත මුරපදය නැවත ඇතුළත් කිරීමට හෝ අලුත් එකක් සැකසීමට සිදුවේ. ඉදිරියට යන්නද?",
|
||||
"Sync passphrase forgotten — all encrypted fields cleared": "සමමුහුර්ත මුරපදය අමතක විය — සියලුම සංකේතාංකිත ක්ෂේත්ර හිස් කරන ලදී",
|
||||
"Sync passphrase": "සමමුහුර්ත මුරපදය",
|
||||
"Encrypts sensitive synced fields (like OPDS catalog credentials) before they leave your device. Set one now or wait — it will be requested when needed.": "ඔබේ උපාංගයෙන් පිටවීමට පෙර සංවේදී සමමුහුර්ත ක්ෂේත්ර (OPDS නාමාවලි අක්තපත්ර වැනි) සංකේතාංකනය කරයි. දැන් එකක් සකසන්න හෝ රැඳී සිටින්න — අවශ්ය වූ විට එය ඉල්ලනු ඇත.",
|
||||
"Set on this account. Will be requested when needed to decrypt synced credentials.": "මෙම ගිණුමේ සකස් කර ඇත. සමමුහුර්ත අක්තපත්ර විකේතනය කිරීම සඳහා අවශ්ය වූ විට එය ඉල්ලනු ඇත.",
|
||||
"Set passphrase": "මුරපදය සකසන්න",
|
||||
"Unlock now": "දැන් අගුළු හරින්න",
|
||||
"Forgot passphrase": "මුරපදය අමතකද",
|
||||
"Incorrect PIN": "වැරදි PIN",
|
||||
"App locked": "යෙදුම අගුළු දමා ඇත",
|
||||
"Enter your PIN": "ඔබගේ PIN ඇතුළත් කරන්න",
|
||||
"Readest is locked. Enter your 4-digit PIN to continue.": "Readest අගුළු දමා ඇත. ඉදිරියට යාමට ඔබගේ ඉලක්කම් 4ක PIN ඇතුළත් කරන්න.",
|
||||
"PIN code": "PIN කේතය",
|
||||
"Forgetting your PIN locks you out of this device. You'll need to clear the app's data to reset it.": "PIN අමතක වීම ඔබව මෙම උපාංගයෙන් අගුළු දමයි. එය යළි පිහිටුවීමට යෙදුමේ දත්ත හිස් කිරීමට අවශ්ය වේ.",
|
||||
"Passphrase must be at least 8 characters": "මුරපදය අවම වශයෙන් අක්ෂර 8 ක් විය යුතුය",
|
||||
"Passphrases do not match": "මුරපද ගැළපෙන්නේ නැත",
|
||||
"Set sync passphrase": "සමමුහුර්ත මුරපදය සකසන්න",
|
||||
"Enter sync passphrase": "සමමුහුර්ත මුරපදය ඇතුළත් කරන්න",
|
||||
"A sync passphrase encrypts your sensitive fields (like OPDS catalog credentials) before they sync. We never see this passphrase. Pick something memorable — there is no recovery without it.": "සමමුහුර්ත මුරපදයක් ඔබේ සංවේදී ක්ෂේත්ර (OPDS නාමාවලි අක්තපත්ර වැනි) සමමුහුර්ත වීමට පෙර සංකේතාංකනය කරයි. අපි කිසිදා මෙම මුරපදය නොදකිමු. මතක තබාගත හැකි දෙයක් තෝරන්න — එය නොමැතිව ප්රතිසාධනයක් නොමැත.",
|
||||
"Enter the sync passphrase you set on another device to decrypt your synced credentials.": "ඔබේ සමමුහුර්ත අක්තපත්ර විකේතනය කිරීමට වෙනත් උපාංගයක ඔබ සැකසූ සමමුහුර්ත මුරපදය ඇතුළත් කරන්න.",
|
||||
"Confirm passphrase": "මුරපදය තහවුරු කරන්න",
|
||||
"Unlock": "අගුළු හරින්න",
|
||||
"Set PIN": "PIN සකසන්න",
|
||||
"Change PIN": "PIN වෙනස් කරන්න",
|
||||
"Disable PIN": "PIN අක්රිය කරන්න",
|
||||
"Pick a 4-digit PIN. You will need to enter it every time you open Readest. There is no way to recover a forgotten PIN — clearing the app data is the only way to reset it.": "ඉලක්කම් 4ක PIN එකක් තෝරන්න. ඔබ Readest විවෘත කරන සෑම අවස්ථාවකම එය ඇතුළත් කළ යුතුය. අමතක වූ PIN යළි ලබා ගැනීමට ක්රමයක් නැත — යෙදුමේ දත්ත හිස් කිරීම පමණක් එය යළි පිහිටුවීමේ ක්රමයයි.",
|
||||
"Enter your current PIN, then choose a new 4-digit PIN.": "ඔබගේ වත්මන් PIN ඇතුළත් කර, පසුව නව ඉලක්කම් 4ක PIN එකක් තෝරන්න.",
|
||||
"Enter your current PIN to disable the app lock.": "යෙදුම් අගුල අක්රිය කිරීමට ඔබගේ වත්මන් PIN ඇතුළත් කරන්න.",
|
||||
"PIN must be {{length}} digits": "PIN ඉලක්කම් {{length}}ක් විය යුතුය",
|
||||
"PINs do not match": "PINs නොගැලපේ",
|
||||
"Current PIN": "වත්මන් PIN",
|
||||
"New PIN": "නව PIN",
|
||||
"Confirm new PIN": "නව PIN තහවුරු කරන්න"
|
||||
}
|
||||
|
||||
@@ -1388,5 +1388,43 @@
|
||||
"Failed to download file: {{title}}": "Prenos datoteke ni uspel: {{title}}",
|
||||
"Failed to delete cloud copy of the file: {{title}}": "Brisanje kopije datoteke v oblaku ni uspelo: {{title}}",
|
||||
"{{percentage}}% used": "{{percentage}} % uporabljeno",
|
||||
"Resets in {{duration}}": "Ponastavi se čez {{duration}}"
|
||||
"Resets in {{duration}}": "Ponastavi se čez {{duration}}",
|
||||
"Set PIN…": "Nastavi PIN…",
|
||||
"Require a 4-digit PIN to open Readest": "Za odpiranje Readest zahtevaj 4-mestni PIN",
|
||||
"Change PIN…": "Spremeni PIN…",
|
||||
"Disable PIN…": "Onemogoči PIN…",
|
||||
"Sync passphrase ready": "Sinhronizacijsko geslo pripravljeno",
|
||||
"This permanently deletes the encrypted credentials we sync (e.g., OPDS catalog passwords) on every device. Local copies are preserved. You will need to re-enter the sync passphrase or set a new one. Continue?": "S tem boste trajno izbrisali šifrirane poverilnice, ki jih sinhroniziramo (npr. gesla kataloga OPDS), v vseh napravah. Lokalne kopije se ohranijo. Znova boste morali vnesti sinhronizacijsko geslo ali nastaviti novo. Nadaljujem?",
|
||||
"Sync passphrase forgotten — all encrypted fields cleared": "Sinhronizacijsko geslo pozabljeno — vsa šifrirana polja so izbrisana",
|
||||
"Sync passphrase": "Sinhronizacijsko geslo",
|
||||
"Encrypts sensitive synced fields (like OPDS catalog credentials) before they leave your device. Set one now or wait — it will be requested when needed.": "Šifrira občutljiva sinhronizirana polja (na primer poverilnice kataloga OPDS), preden zapustijo vašo napravo. Nastavite ga zdaj ali počakajte — zahtevan bo, ko bo potreben.",
|
||||
"Set on this account. Will be requested when needed to decrypt synced credentials.": "Nastavljeno za ta račun. Zahtevano bo, ko bo potrebno za dešifriranje sinhroniziranih poverilnic.",
|
||||
"Set passphrase": "Nastavi geslo",
|
||||
"Unlock now": "Odkleni zdaj",
|
||||
"Forgot passphrase": "Pozabljeno geslo",
|
||||
"Incorrect PIN": "Napačen PIN",
|
||||
"App locked": "Aplikacija je zaklenjena",
|
||||
"Enter your PIN": "Vnesite svoj PIN",
|
||||
"Readest is locked. Enter your 4-digit PIN to continue.": "Readest je zaklenjen. Vnesite svoj 4-mestni PIN, da nadaljujete.",
|
||||
"PIN code": "Koda PIN",
|
||||
"Forgetting your PIN locks you out of this device. You'll need to clear the app's data to reset it.": "Če pozabite PIN, boste zaklenjeni iz te naprave. Za ponastavitev boste morali počistiti podatke aplikacije.",
|
||||
"Passphrase must be at least 8 characters": "Geslo mora imeti vsaj 8 znakov",
|
||||
"Passphrases do not match": "Gesli se ne ujemata",
|
||||
"Set sync passphrase": "Nastavi sinhronizacijsko geslo",
|
||||
"Enter sync passphrase": "Vnesite sinhronizacijsko geslo",
|
||||
"A sync passphrase encrypts your sensitive fields (like OPDS catalog credentials) before they sync. We never see this passphrase. Pick something memorable — there is no recovery without it.": "Sinhronizacijsko geslo šifrira vaša občutljiva polja (na primer poverilnice kataloga OPDS), preden se sinhronizirajo. Tega gesla nikoli ne vidimo. Izberite nekaj nepozabnega — brez njega ni obnovitve.",
|
||||
"Enter the sync passphrase you set on another device to decrypt your synced credentials.": "Vnesite sinhronizacijsko geslo, ki ste ga nastavili v drugi napravi, da dešifrirate sinhronizirane poverilnice.",
|
||||
"Confirm passphrase": "Potrdi geslo",
|
||||
"Unlock": "Odkleni",
|
||||
"Set PIN": "Nastavi PIN",
|
||||
"Change PIN": "Spremeni PIN",
|
||||
"Disable PIN": "Onemogoči PIN",
|
||||
"Pick a 4-digit PIN. You will need to enter it every time you open Readest. There is no way to recover a forgotten PIN — clearing the app data is the only way to reset it.": "Izberite 4-mestni PIN. Vnesti ga boste morali vsakič, ko odprete Readest. Pozabljenega PIN-a ni mogoče obnoviti — čiščenje podatkov aplikacije je edini način za ponastavitev.",
|
||||
"Enter your current PIN, then choose a new 4-digit PIN.": "Vnesite svoj trenutni PIN in nato izberite nov 4-mestni PIN.",
|
||||
"Enter your current PIN to disable the app lock.": "Vnesite svoj trenutni PIN, da onemogočite zaklep aplikacije.",
|
||||
"PIN must be {{length}} digits": "PIN mora vsebovati {{length}} števk",
|
||||
"PINs do not match": "PIN-i se ne ujemajo",
|
||||
"Current PIN": "Trenutni PIN",
|
||||
"New PIN": "Nov PIN",
|
||||
"Confirm new PIN": "Potrdite nov PIN"
|
||||
}
|
||||
|
||||
@@ -1340,5 +1340,43 @@
|
||||
"Failed to download file: {{title}}": "Kunde inte ladda ner fil: {{title}}",
|
||||
"Failed to delete cloud copy of the file: {{title}}": "Kunde inte radera molnkopia av fil: {{title}}",
|
||||
"{{percentage}}% used": "{{percentage}} % använt",
|
||||
"Resets in {{duration}}": "Återställs om {{duration}}"
|
||||
"Resets in {{duration}}": "Återställs om {{duration}}",
|
||||
"Set PIN…": "Ange PIN…",
|
||||
"Require a 4-digit PIN to open Readest": "Kräv en 4-siffrig PIN för att öppna Readest",
|
||||
"Change PIN…": "Ändra PIN…",
|
||||
"Disable PIN…": "Inaktivera PIN…",
|
||||
"Sync passphrase ready": "Synkroniseringslösenfras redo",
|
||||
"This permanently deletes the encrypted credentials we sync (e.g., OPDS catalog passwords) on every device. Local copies are preserved. You will need to re-enter the sync passphrase or set a new one. Continue?": "Detta tar permanent bort de krypterade autentiseringsuppgifterna som vi synkroniserar (t.ex. lösenord för OPDS-kataloger) på alla enheter. Lokala kopior bevaras. Du behöver ange synkroniseringslösenfrasen igen eller välja en ny. Fortsätta?",
|
||||
"Sync passphrase forgotten — all encrypted fields cleared": "Synkroniseringslösenfrasen glömdes — alla krypterade fält rensade",
|
||||
"Sync passphrase": "Synkroniseringslösenfras",
|
||||
"Encrypts sensitive synced fields (like OPDS catalog credentials) before they leave your device. Set one now or wait — it will be requested when needed.": "Krypterar känsliga synkroniserade fält (t.ex. OPDS-katalogautentiseringsuppgifter) innan de lämnar din enhet. Ange en nu eller vänta — den efterfrågas när det behövs.",
|
||||
"Set on this account. Will be requested when needed to decrypt synced credentials.": "Inställd för det här kontot. Efterfrågas när det behövs för att dekryptera synkroniserade autentiseringsuppgifter.",
|
||||
"Set passphrase": "Ange lösenfras",
|
||||
"Unlock now": "Lås upp nu",
|
||||
"Forgot passphrase": "Glömt lösenfras",
|
||||
"Incorrect PIN": "Felaktig PIN",
|
||||
"App locked": "Appen är låst",
|
||||
"Enter your PIN": "Ange din PIN",
|
||||
"Readest is locked. Enter your 4-digit PIN to continue.": "Readest är låst. Ange din 4-siffriga PIN för att fortsätta.",
|
||||
"PIN code": "PIN-kod",
|
||||
"Forgetting your PIN locks you out of this device. You'll need to clear the app's data to reset it.": "Om du glömmer din PIN låses du ute från den här enheten. Du måste rensa appens data för att återställa den.",
|
||||
"Passphrase must be at least 8 characters": "Lösenfrasen måste vara minst 8 tecken",
|
||||
"Passphrases do not match": "Lösenfraserna matchar inte",
|
||||
"Set sync passphrase": "Ange synkroniseringslösenfras",
|
||||
"Enter sync passphrase": "Ange synkroniseringslösenfras",
|
||||
"A sync passphrase encrypts your sensitive fields (like OPDS catalog credentials) before they sync. We never see this passphrase. Pick something memorable — there is no recovery without it.": "En synkroniseringslösenfras krypterar dina känsliga fält (som OPDS-katalogautentiseringsuppgifter) innan de synkroniseras. Vi ser aldrig den här lösenfrasen. Välj något minnesvärt — det går inte att återställa utan den.",
|
||||
"Enter the sync passphrase you set on another device to decrypt your synced credentials.": "Ange synkroniseringslösenfrasen du angav på en annan enhet för att dekryptera dina synkroniserade autentiseringsuppgifter.",
|
||||
"Confirm passphrase": "Bekräfta lösenfras",
|
||||
"Unlock": "Lås upp",
|
||||
"Set PIN": "Ange PIN",
|
||||
"Change PIN": "Ändra PIN",
|
||||
"Disable PIN": "Inaktivera PIN",
|
||||
"Pick a 4-digit PIN. You will need to enter it every time you open Readest. There is no way to recover a forgotten PIN — clearing the app data is the only way to reset it.": "Välj en 4-siffrig PIN. Du måste ange den varje gång du öppnar Readest. Det går inte att återställa en glömd PIN — att rensa appdata är det enda sättet att återställa den.",
|
||||
"Enter your current PIN, then choose a new 4-digit PIN.": "Ange din nuvarande PIN och välj sedan en ny 4-siffrig PIN.",
|
||||
"Enter your current PIN to disable the app lock.": "Ange din nuvarande PIN för att inaktivera applåset.",
|
||||
"PIN must be {{length}} digits": "PIN måste vara {{length}} siffror",
|
||||
"PINs do not match": "PIN-koderna stämmer inte överens",
|
||||
"Current PIN": "Nuvarande PIN",
|
||||
"New PIN": "Ny PIN",
|
||||
"Confirm new PIN": "Bekräfta ny PIN"
|
||||
}
|
||||
|
||||
@@ -1340,5 +1340,43 @@
|
||||
"Failed to download file: {{title}}": "கோப்பை பதிவிறக்க முடியவில்லை: {{title}}",
|
||||
"Failed to delete cloud copy of the file: {{title}}": "கோப்பின் cloud நகலை நீக்க முடியவில்லை: {{title}}",
|
||||
"{{percentage}}% used": "{{percentage}}% பயன்படுத்தப்பட்டது",
|
||||
"Resets in {{duration}}": "{{duration}} இல் மீட்டமைக்கப்படும்"
|
||||
"Resets in {{duration}}": "{{duration}} இல் மீட்டமைக்கப்படும்",
|
||||
"Set PIN…": "PIN ஐ அமை…",
|
||||
"Require a 4-digit PIN to open Readest": "Readest ஐ திறக்க 4-இலக்க PIN ஐ கேள்",
|
||||
"Change PIN…": "PIN ஐ மாற்று…",
|
||||
"Disable PIN…": "PIN ஐ முடக்கு…",
|
||||
"Sync passphrase ready": "ஒத்திசைவு கடவுச்சொற்றொடர் தயார்",
|
||||
"This permanently deletes the encrypted credentials we sync (e.g., OPDS catalog passwords) on every device. Local copies are preserved. You will need to re-enter the sync passphrase or set a new one. Continue?": "இது ஒவ்வொரு சாதனத்திலும் நாங்கள் ஒத்திசைக்கும் குறியாக்கம் செய்யப்பட்ட சான்றுகளை (எ.கா., OPDS பட்டியல் கடவுச்சொற்கள்) நிரந்தரமாக நீக்குகிறது. உள்ளூர் நகல்கள் பாதுகாக்கப்படுகின்றன. நீங்கள் ஒத்திசைவு கடவுச்சொற்றொடரை மீண்டும் உள்ளிட வேண்டும் அல்லது புதியதை அமைக்க வேண்டும். தொடரவா?",
|
||||
"Sync passphrase forgotten — all encrypted fields cleared": "ஒத்திசைவு கடவுச்சொற்றொடர் மறக்கப்பட்டது — அனைத்து குறியாக்கம் செய்யப்பட்ட புலங்களும் அழிக்கப்பட்டன",
|
||||
"Sync passphrase": "ஒத்திசைவு கடவுச்சொற்றொடர்",
|
||||
"Encrypts sensitive synced fields (like OPDS catalog credentials) before they leave your device. Set one now or wait — it will be requested when needed.": "உங்கள் சாதனத்தை விட்டு வெளியேறுவதற்கு முன், முக்கியமான ஒத்திசைக்கப்பட்ட புலங்களை (OPDS பட்டியல் சான்றுகள் போன்றவை) குறியாக்கம் செய்கிறது. இப்போது ஒன்றை அமைக்கவும் அல்லது காத்திருக்கவும் — தேவைப்படும் போது கேட்கப்படும்.",
|
||||
"Set on this account. Will be requested when needed to decrypt synced credentials.": "இந்த கணக்கில் அமைக்கப்பட்டுள்ளது. ஒத்திசைக்கப்பட்ட சான்றுகளை விகுதியாக்க தேவைப்படும்போது கேட்கப்படும்.",
|
||||
"Set passphrase": "கடவுச்சொற்றொடரை அமைக்கவும்",
|
||||
"Unlock now": "இப்போது பூட்டை திறக்கவும்",
|
||||
"Forgot passphrase": "கடவுச்சொற்றொடரை மறந்துவிட்டீர்களா",
|
||||
"Incorrect PIN": "தவறான PIN",
|
||||
"App locked": "செயலி பூட்டப்பட்டுள்ளது",
|
||||
"Enter your PIN": "உங்கள் PIN ஐ உள்ளிடவும்",
|
||||
"Readest is locked. Enter your 4-digit PIN to continue.": "Readest பூட்டப்பட்டுள்ளது. தொடர உங்கள் 4-இலக்க PIN ஐ உள்ளிடவும்.",
|
||||
"PIN code": "PIN குறியீடு",
|
||||
"Forgetting your PIN locks you out of this device. You'll need to clear the app's data to reset it.": "PIN ஐ மறந்தால் இந்த சாதனத்திலிருந்து நீங்கள் வெளியேற்றப்படுவீர்கள். அதை மீட்டமைக்க செயலியின் தரவை அழிக்க வேண்டும்.",
|
||||
"Passphrase must be at least 8 characters": "கடவுச்சொற்றொடர் குறைந்தது 8 எழுத்துகளாக இருக்க வேண்டும்",
|
||||
"Passphrases do not match": "கடவுச்சொற்றொடர்கள் பொருந்தவில்லை",
|
||||
"Set sync passphrase": "ஒத்திசைவு கடவுச்சொற்றொடரை அமைக்கவும்",
|
||||
"Enter sync passphrase": "ஒத்திசைவு கடவுச்சொற்றொடரை உள்ளிடவும்",
|
||||
"A sync passphrase encrypts your sensitive fields (like OPDS catalog credentials) before they sync. We never see this passphrase. Pick something memorable — there is no recovery without it.": "ஒத்திசைவு கடவுச்சொற்றொடர், ஒத்திசைக்கப்படுவதற்கு முன், உங்கள் முக்கியமான புலங்களை (OPDS பட்டியல் சான்றுகள் போன்றவை) குறியாக்கம் செய்கிறது. இந்த கடவுச்சொற்றொடரை நாங்கள் ஒருபோதும் காண்பதில்லை. நினைவில் கொள்ளக்கூடிய ஒன்றைத் தேர்ந்தெடுக்கவும் — அதன்றி மீட்டெடுப்பு இல்லை.",
|
||||
"Enter the sync passphrase you set on another device to decrypt your synced credentials.": "உங்கள் ஒத்திசைக்கப்பட்ட சான்றுகளை விகுதியாக்க, மற்றொரு சாதனத்தில் நீங்கள் அமைத்த ஒத்திசைவு கடவுச்சொற்றொடரை உள்ளிடவும்.",
|
||||
"Confirm passphrase": "கடவுச்சொற்றொடரை உறுதிப்படுத்தவும்",
|
||||
"Unlock": "பூட்டை திறக்கவும்",
|
||||
"Set PIN": "PIN ஐ அமை",
|
||||
"Change PIN": "PIN ஐ மாற்று",
|
||||
"Disable PIN": "PIN ஐ முடக்கு",
|
||||
"Pick a 4-digit PIN. You will need to enter it every time you open Readest. There is no way to recover a forgotten PIN — clearing the app data is the only way to reset it.": "4-இலக்க PIN ஐ தேர்ந்தெடுக்கவும். Readest ஐ திறக்கும் ஒவ்வொரு முறையும் அதை உள்ளிட வேண்டும். மறந்துபோன PIN ஐ மீட்டெடுக்க வழி இல்லை — செயலியின் தரவை அழிப்பதே அதை மீட்டமைக்கும் ஒரே வழி.",
|
||||
"Enter your current PIN, then choose a new 4-digit PIN.": "உங்கள் தற்போதைய PIN ஐ உள்ளிட்டு, பின்னர் புதிய 4-இலக்க PIN ஐ தேர்ந்தெடுக்கவும்.",
|
||||
"Enter your current PIN to disable the app lock.": "செயலி பூட்டை முடக்க உங்கள் தற்போதைய PIN ஐ உள்ளிடவும்.",
|
||||
"PIN must be {{length}} digits": "PIN {{length}} இலக்கங்களாக இருக்க வேண்டும்",
|
||||
"PINs do not match": "PIN கள் பொருந்தவில்லை",
|
||||
"Current PIN": "தற்போதைய PIN",
|
||||
"New PIN": "புதிய PIN",
|
||||
"Confirm new PIN": "புதிய PIN ஐ உறுதிப்படுத்து"
|
||||
}
|
||||
|
||||
@@ -1316,5 +1316,43 @@
|
||||
"Failed to download file: {{title}}": "ไม่สามารถดาวน์โหลดไฟล์: {{title}}",
|
||||
"Failed to delete cloud copy of the file: {{title}}": "ไม่สามารถลบสำเนาไฟล์บนคลาวด์: {{title}}",
|
||||
"{{percentage}}% used": "ใช้ไปแล้ว {{percentage}}%",
|
||||
"Resets in {{duration}}": "รีเซ็ตใน {{duration}}"
|
||||
"Resets in {{duration}}": "รีเซ็ตใน {{duration}}",
|
||||
"Set PIN…": "ตั้งค่า PIN…",
|
||||
"Require a 4-digit PIN to open Readest": "ต้องใช้ PIN 4 หลักเพื่อเปิด Readest",
|
||||
"Change PIN…": "เปลี่ยน PIN…",
|
||||
"Disable PIN…": "ปิดใช้งาน PIN…",
|
||||
"Sync passphrase ready": "วลีรหัสผ่านสำหรับซิงค์พร้อมแล้ว",
|
||||
"This permanently deletes the encrypted credentials we sync (e.g., OPDS catalog passwords) on every device. Local copies are preserved. You will need to re-enter the sync passphrase or set a new one. Continue?": "การกระทำนี้จะลบข้อมูลรับรองที่เข้ารหัสซึ่งเราซิงค์ไว้ (เช่น รหัสผ่านของแคตตาล็อก OPDS) บนทุกอุปกรณ์อย่างถาวร สำเนาที่อยู่ในเครื่องจะถูกเก็บไว้ คุณจะต้องป้อนวลีรหัสผ่านสำหรับซิงค์อีกครั้งหรือตั้งค่าใหม่ ดำเนินการต่อหรือไม่?",
|
||||
"Sync passphrase forgotten — all encrypted fields cleared": "ลืมวลีรหัสผ่านสำหรับซิงค์ — ฟิลด์ที่เข้ารหัสทั้งหมดถูกล้างแล้ว",
|
||||
"Sync passphrase": "วลีรหัสผ่านสำหรับซิงค์",
|
||||
"Encrypts sensitive synced fields (like OPDS catalog credentials) before they leave your device. Set one now or wait — it will be requested when needed.": "เข้ารหัสฟิลด์ที่ซิงค์ซึ่งมีข้อมูลละเอียดอ่อน (เช่น ข้อมูลรับรองแคตตาล็อก OPDS) ก่อนออกจากอุปกรณ์ของคุณ ตั้งค่าเดี๋ยวนี้หรือรอไว้ก่อน — ระบบจะร้องขอเมื่อจำเป็น",
|
||||
"Set on this account. Will be requested when needed to decrypt synced credentials.": "ตั้งค่าไว้ในบัญชีนี้แล้ว จะถูกร้องขอเมื่อจำเป็นต้องถอดรหัสข้อมูลรับรองที่ซิงค์ไว้",
|
||||
"Set passphrase": "ตั้งค่าวลีรหัสผ่าน",
|
||||
"Unlock now": "ปลดล็อกตอนนี้",
|
||||
"Forgot passphrase": "ลืมวลีรหัสผ่าน",
|
||||
"Incorrect PIN": "PIN ไม่ถูกต้อง",
|
||||
"App locked": "แอปถูกล็อก",
|
||||
"Enter your PIN": "ใส่ PIN ของคุณ",
|
||||
"Readest is locked. Enter your 4-digit PIN to continue.": "Readest ถูกล็อก ใส่ PIN 4 หลักของคุณเพื่อดำเนินการต่อ",
|
||||
"PIN code": "รหัส PIN",
|
||||
"Forgetting your PIN locks you out of this device. You'll need to clear the app's data to reset it.": "การลืม PIN จะทำให้คุณถูกล็อกออกจากอุปกรณ์นี้ คุณต้องล้างข้อมูลแอปเพื่อรีเซ็ต",
|
||||
"Passphrase must be at least 8 characters": "วลีรหัสผ่านต้องมีอย่างน้อย 8 ตัวอักษร",
|
||||
"Passphrases do not match": "วลีรหัสผ่านไม่ตรงกัน",
|
||||
"Set sync passphrase": "ตั้งค่าวลีรหัสผ่านสำหรับซิงค์",
|
||||
"Enter sync passphrase": "ป้อนวลีรหัสผ่านสำหรับซิงค์",
|
||||
"A sync passphrase encrypts your sensitive fields (like OPDS catalog credentials) before they sync. We never see this passphrase. Pick something memorable — there is no recovery without it.": "วลีรหัสผ่านสำหรับซิงค์จะเข้ารหัสฟิลด์ที่มีข้อมูลละเอียดอ่อนของคุณ (เช่น ข้อมูลรับรองแคตตาล็อก OPDS) ก่อนที่จะซิงค์ เราไม่เคยเห็นวลีรหัสผ่านนี้ เลือกสิ่งที่จดจำง่าย — ไม่มีการกู้คืนหากปราศจากวลีนี้",
|
||||
"Enter the sync passphrase you set on another device to decrypt your synced credentials.": "ป้อนวลีรหัสผ่านสำหรับซิงค์ที่คุณตั้งไว้บนอุปกรณ์อื่นเพื่อถอดรหัสข้อมูลรับรองที่ซิงค์ไว้",
|
||||
"Confirm passphrase": "ยืนยันวลีรหัสผ่าน",
|
||||
"Unlock": "ปลดล็อก",
|
||||
"Set PIN": "ตั้งค่า PIN",
|
||||
"Change PIN": "เปลี่ยน PIN",
|
||||
"Disable PIN": "ปิดใช้งาน PIN",
|
||||
"Pick a 4-digit PIN. You will need to enter it every time you open Readest. There is no way to recover a forgotten PIN — clearing the app data is the only way to reset it.": "เลือก PIN 4 หลัก คุณต้องใส่ทุกครั้งที่เปิด Readest ไม่มีวิธีกู้คืน PIN ที่ลืม — การล้างข้อมูลแอปเป็นวิธีเดียวในการรีเซ็ต",
|
||||
"Enter your current PIN, then choose a new 4-digit PIN.": "ใส่ PIN ปัจจุบัน จากนั้นเลือก PIN 4 หลักใหม่",
|
||||
"Enter your current PIN to disable the app lock.": "ใส่ PIN ปัจจุบันเพื่อปิดใช้งานการล็อกแอป",
|
||||
"PIN must be {{length}} digits": "PIN ต้องมี {{length}} หลัก",
|
||||
"PINs do not match": "PIN ไม่ตรงกัน",
|
||||
"Current PIN": "PIN ปัจจุบัน",
|
||||
"New PIN": "PIN ใหม่",
|
||||
"Confirm new PIN": "ยืนยัน PIN ใหม่"
|
||||
}
|
||||
|
||||
@@ -1340,5 +1340,43 @@
|
||||
"Failed to download file: {{title}}": "Dosya indirilemedi: {{title}}",
|
||||
"Failed to delete cloud copy of the file: {{title}}": "Dosyanın bulut kopyasını silme başarısız oldu: {{title}}",
|
||||
"{{percentage}}% used": "%{{percentage}} kullanıldı",
|
||||
"Resets in {{duration}}": "{{duration}} sonra sıfırlanır"
|
||||
"Resets in {{duration}}": "{{duration}} sonra sıfırlanır",
|
||||
"Set PIN…": "PIN Belirle…",
|
||||
"Require a 4-digit PIN to open Readest": "Readest'i açmak için 4 haneli PIN iste",
|
||||
"Change PIN…": "PIN Değiştir…",
|
||||
"Disable PIN…": "PIN'i Devre Dışı Bırak…",
|
||||
"Sync passphrase ready": "Eşitleme parolası hazır",
|
||||
"This permanently deletes the encrypted credentials we sync (e.g., OPDS catalog passwords) on every device. Local copies are preserved. You will need to re-enter the sync passphrase or set a new one. Continue?": "Bu işlem, her cihazda eşitlediğimiz şifrelenmiş kimlik bilgilerini (ör. OPDS kataloğu parolaları) kalıcı olarak siler. Yerel kopyalar korunur. Eşitleme parolasını yeniden girmeniz veya yeni bir tane belirlemeniz gerekecek. Devam edilsin mi?",
|
||||
"Sync passphrase forgotten — all encrypted fields cleared": "Eşitleme parolası unutuldu — tüm şifrelenmiş alanlar temizlendi",
|
||||
"Sync passphrase": "Eşitleme parolası",
|
||||
"Encrypts sensitive synced fields (like OPDS catalog credentials) before they leave your device. Set one now or wait — it will be requested when needed.": "Hassas eşitlenen alanları (OPDS kataloğu kimlik bilgileri gibi) cihazınızdan ayrılmadan önce şifreler. Şimdi bir tane belirleyin ya da bekleyin — gerektiğinde istenecektir.",
|
||||
"Set on this account. Will be requested when needed to decrypt synced credentials.": "Bu hesapta ayarlandı. Eşitlenen kimlik bilgilerinin şifresini çözmek için gerektiğinde istenecektir.",
|
||||
"Set passphrase": "Parolayı belirle",
|
||||
"Unlock now": "Şimdi kilidi aç",
|
||||
"Forgot passphrase": "Parolayı unuttum",
|
||||
"Incorrect PIN": "Yanlış PIN",
|
||||
"App locked": "Uygulama kilitli",
|
||||
"Enter your PIN": "PIN'inizi girin",
|
||||
"Readest is locked. Enter your 4-digit PIN to continue.": "Readest kilitli. Devam etmek için 4 haneli PIN'inizi girin.",
|
||||
"PIN code": "PIN kodu",
|
||||
"Forgetting your PIN locks you out of this device. You'll need to clear the app's data to reset it.": "PIN'inizi unutmak sizi bu cihazdan kilitler. Sıfırlamak için uygulamanın verilerini temizlemeniz gerekir.",
|
||||
"Passphrase must be at least 8 characters": "Parola en az 8 karakter olmalıdır",
|
||||
"Passphrases do not match": "Parolalar eşleşmiyor",
|
||||
"Set sync passphrase": "Eşitleme parolasını belirle",
|
||||
"Enter sync passphrase": "Eşitleme parolasını girin",
|
||||
"A sync passphrase encrypts your sensitive fields (like OPDS catalog credentials) before they sync. We never see this passphrase. Pick something memorable — there is no recovery without it.": "Eşitleme parolası, hassas alanlarınızı (örneğin OPDS kataloğu kimlik bilgilerinizi) eşitlenmeden önce şifreler. Bu parolayı asla görmeyiz. Akılda kalıcı bir şey seçin — onsuz kurtarma yoktur.",
|
||||
"Enter the sync passphrase you set on another device to decrypt your synced credentials.": "Eşitlenen kimlik bilgilerinizin şifresini çözmek için başka bir cihazda belirlediğiniz eşitleme parolasını girin.",
|
||||
"Confirm passphrase": "Parolayı onayla",
|
||||
"Unlock": "Kilidi aç",
|
||||
"Set PIN": "PIN Belirle",
|
||||
"Change PIN": "PIN Değiştir",
|
||||
"Disable PIN": "PIN'i Devre Dışı Bırak",
|
||||
"Pick a 4-digit PIN. You will need to enter it every time you open Readest. There is no way to recover a forgotten PIN — clearing the app data is the only way to reset it.": "4 haneli bir PIN seçin. Readest'i her açtığınızda bunu girmeniz gerekecek. Unutulan bir PIN'i kurtarmanın bir yolu yoktur — uygulama verilerini temizlemek sıfırlamanın tek yoludur.",
|
||||
"Enter your current PIN, then choose a new 4-digit PIN.": "Mevcut PIN'inizi girin, ardından yeni bir 4 haneli PIN seçin.",
|
||||
"Enter your current PIN to disable the app lock.": "Uygulama kilidini devre dışı bırakmak için mevcut PIN'inizi girin.",
|
||||
"PIN must be {{length}} digits": "PIN {{length}} haneli olmalıdır",
|
||||
"PINs do not match": "PIN'ler eşleşmiyor",
|
||||
"Current PIN": "Mevcut PIN",
|
||||
"New PIN": "Yeni PIN",
|
||||
"Confirm new PIN": "Yeni PIN'i onayla"
|
||||
}
|
||||
|
||||
@@ -1388,5 +1388,43 @@
|
||||
"Failed to download file: {{title}}": "Не вдалося завантажити файл: {{title}}",
|
||||
"Failed to delete cloud copy of the file: {{title}}": "Не вдалося видалити копію файлу в хмарі: {{title}}",
|
||||
"{{percentage}}% used": "Використано {{percentage}}%",
|
||||
"Resets in {{duration}}": "Скидання через {{duration}}"
|
||||
"Resets in {{duration}}": "Скидання через {{duration}}",
|
||||
"Set PIN…": "Установити PIN…",
|
||||
"Require a 4-digit PIN to open Readest": "Вимагати 4-значний PIN для відкриття Readest",
|
||||
"Change PIN…": "Змінити PIN…",
|
||||
"Disable PIN…": "Вимкнути PIN…",
|
||||
"Sync passphrase ready": "Парольну фразу синхронізації готово",
|
||||
"This permanently deletes the encrypted credentials we sync (e.g., OPDS catalog passwords) on every device. Local copies are preserved. You will need to re-enter the sync passphrase or set a new one. Continue?": "Це остаточно видалить зашифровані облікові дані, які ми синхронізуємо (наприклад, паролі каталогу OPDS), на кожному пристрої. Локальні копії зберігаються. Вам потрібно буде повторно ввести парольну фразу синхронізації або встановити нову. Продовжити?",
|
||||
"Sync passphrase forgotten — all encrypted fields cleared": "Парольну фразу синхронізації забуто — усі зашифровані поля очищено",
|
||||
"Sync passphrase": "Парольна фраза синхронізації",
|
||||
"Encrypts sensitive synced fields (like OPDS catalog credentials) before they leave your device. Set one now or wait — it will be requested when needed.": "Шифрує конфіденційні синхронізовані поля (наприклад, облікові дані каталогу OPDS) перед тим, як вони залишать ваш пристрій. Установіть її зараз або зачекайте — її буде запитано за потреби.",
|
||||
"Set on this account. Will be requested when needed to decrypt synced credentials.": "Установлено для цього облікового запису. Запитуватиметься за потреби для розшифрування синхронізованих облікових даних.",
|
||||
"Set passphrase": "Установити парольну фразу",
|
||||
"Unlock now": "Розблокувати зараз",
|
||||
"Forgot passphrase": "Забули парольну фразу",
|
||||
"Incorrect PIN": "Неправильний PIN",
|
||||
"App locked": "Програму заблоковано",
|
||||
"Enter your PIN": "Введіть свій PIN",
|
||||
"Readest is locked. Enter your 4-digit PIN to continue.": "Readest заблоковано. Введіть свій 4-значний PIN, щоб продовжити.",
|
||||
"PIN code": "PIN-код",
|
||||
"Forgetting your PIN locks you out of this device. You'll need to clear the app's data to reset it.": "Якщо ви забудете PIN, ви втратите доступ до цього пристрою. Щоб скинути його, доведеться очистити дані програми.",
|
||||
"Passphrase must be at least 8 characters": "Парольна фраза повинна містити щонайменше 8 символів",
|
||||
"Passphrases do not match": "Парольні фрази не збігаються",
|
||||
"Set sync passphrase": "Установити парольну фразу синхронізації",
|
||||
"Enter sync passphrase": "Уведіть парольну фразу синхронізації",
|
||||
"A sync passphrase encrypts your sensitive fields (like OPDS catalog credentials) before they sync. We never see this passphrase. Pick something memorable — there is no recovery without it.": "Парольна фраза синхронізації шифрує ваші конфіденційні поля (наприклад, облікові дані каталогу OPDS) перед синхронізацією. Ми ніколи не бачимо цю парольну фразу. Виберіть щось, що ви запам’ятаєте — без неї відновлення неможливе.",
|
||||
"Enter the sync passphrase you set on another device to decrypt your synced credentials.": "Уведіть парольну фразу синхронізації, яку ви встановили на іншому пристрої, щоб розшифрувати синхронізовані облікові дані.",
|
||||
"Confirm passphrase": "Підтвердьте парольну фразу",
|
||||
"Unlock": "Розблокувати",
|
||||
"Set PIN": "Установити PIN",
|
||||
"Change PIN": "Змінити PIN",
|
||||
"Disable PIN": "Вимкнути PIN",
|
||||
"Pick a 4-digit PIN. You will need to enter it every time you open Readest. There is no way to recover a forgotten PIN — clearing the app data is the only way to reset it.": "Виберіть 4-значний PIN. Вам потрібно буде вводити його щоразу, коли ви відкриваєте Readest. Відновити забутий PIN неможливо — очищення даних програми — єдиний спосіб скинути його.",
|
||||
"Enter your current PIN, then choose a new 4-digit PIN.": "Введіть поточний PIN, а потім виберіть новий 4-значний PIN.",
|
||||
"Enter your current PIN to disable the app lock.": "Введіть поточний PIN, щоб вимкнути блокування програми.",
|
||||
"PIN must be {{length}} digits": "PIN має містити {{length}} цифр",
|
||||
"PINs do not match": "PIN-коди не збігаються",
|
||||
"Current PIN": "Поточний PIN",
|
||||
"New PIN": "Новий PIN",
|
||||
"Confirm new PIN": "Підтвердити новий PIN"
|
||||
}
|
||||
|
||||
@@ -1340,5 +1340,43 @@
|
||||
"Failed to download file: {{title}}": "Faylni yuklab boʻlmadi: {{title}}",
|
||||
"Failed to delete cloud copy of the file: {{title}}": "Faylning bulutdagi nusxasini oʻchirib boʻlmadi: {{title}}",
|
||||
"{{percentage}}% used": "{{percentage}}% ishlatilgan",
|
||||
"Resets in {{duration}}": "{{duration}} dan keyin tiklanadi"
|
||||
"Resets in {{duration}}": "{{duration}} dan keyin tiklanadi",
|
||||
"Set PIN…": "PIN o'rnatish…",
|
||||
"Require a 4-digit PIN to open Readest": "Readest'ni ochish uchun 4 xonali PIN talab qilish",
|
||||
"Change PIN…": "PIN'ni o'zgartirish…",
|
||||
"Disable PIN…": "PIN'ni o'chirish…",
|
||||
"Sync passphrase ready": "Sinxronlash parol iborasi tayyor",
|
||||
"This permanently deletes the encrypted credentials we sync (e.g., OPDS catalog passwords) on every device. Local copies are preserved. You will need to re-enter the sync passphrase or set a new one. Continue?": "Bu har bir qurilmada biz sinxronlaydigan shifrlangan hisob ma’lumotlarini (masalan, OPDS katalogi parollarini) butunlay o‘chiradi. Mahalliy nusxalar saqlanadi. Sinxronlash parol iborasini qaytadan kiritishingiz yoki yangisini o‘rnatishingiz kerak bo‘ladi. Davom etilsinmi?",
|
||||
"Sync passphrase forgotten — all encrypted fields cleared": "Sinxronlash parol iborasi unutildi — barcha shifrlangan maydonlar tozalandi",
|
||||
"Sync passphrase": "Sinxronlash parol iborasi",
|
||||
"Encrypts sensitive synced fields (like OPDS catalog credentials) before they leave your device. Set one now or wait — it will be requested when needed.": "Maxfiy sinxronlangan maydonlarni (masalan, OPDS katalogi hisob ma’lumotlari) qurilmangizdan chiqishidan oldin shifrlaydi. Hozir o‘rnating yoki kuting — kerak bo‘lganda so‘raladi.",
|
||||
"Set on this account. Will be requested when needed to decrypt synced credentials.": "Ushbu hisobda o‘rnatilgan. Sinxronlangan hisob ma’lumotlarini deshifrlash uchun kerak bo‘lganda so‘raladi.",
|
||||
"Set passphrase": "Parol iborasini o‘rnatish",
|
||||
"Unlock now": "Hozir qulfdan chiqarish",
|
||||
"Forgot passphrase": "Parol iborasini unutdingizmi",
|
||||
"Incorrect PIN": "Noto'g'ri PIN",
|
||||
"App locked": "Ilova qulflangan",
|
||||
"Enter your PIN": "PIN'ingizni kiriting",
|
||||
"Readest is locked. Enter your 4-digit PIN to continue.": "Readest qulflangan. Davom etish uchun 4 xonali PIN'ingizni kiriting.",
|
||||
"PIN code": "PIN kodi",
|
||||
"Forgetting your PIN locks you out of this device. You'll need to clear the app's data to reset it.": "PIN'ni unutsangiz, ushbu qurilmadan chiqib qolasiz. Uni qayta tiklash uchun ilova ma'lumotlarini tozalashingiz kerak bo'ladi.",
|
||||
"Passphrase must be at least 8 characters": "Parol iborasi kamida 8 ta belgidan iborat bo‘lishi kerak",
|
||||
"Passphrases do not match": "Parol iboralari mos kelmaydi",
|
||||
"Set sync passphrase": "Sinxronlash parol iborasini o‘rnatish",
|
||||
"Enter sync passphrase": "Sinxronlash parol iborasini kiriting",
|
||||
"A sync passphrase encrypts your sensitive fields (like OPDS catalog credentials) before they sync. We never see this passphrase. Pick something memorable — there is no recovery without it.": "Sinxronlash parol iborasi maxfiy maydonlaringizni (masalan, OPDS katalogi hisob ma’lumotlarini) sinxronlanishidan oldin shifrlaydi. Biz bu parol iborasini hech qachon ko‘rmaymiz. Eslab qoladigan narsa tanlang — usiz tiklash imkonsiz.",
|
||||
"Enter the sync passphrase you set on another device to decrypt your synced credentials.": "Sinxronlangan hisob ma’lumotlarini deshifrlash uchun boshqa qurilmada o‘rnatgan sinxronlash parol iborangizni kiriting.",
|
||||
"Confirm passphrase": "Parol iborasini tasdiqlang",
|
||||
"Unlock": "Qulfdan chiqarish",
|
||||
"Set PIN": "PIN o'rnatish",
|
||||
"Change PIN": "PIN'ni o'zgartirish",
|
||||
"Disable PIN": "PIN'ni o'chirish",
|
||||
"Pick a 4-digit PIN. You will need to enter it every time you open Readest. There is no way to recover a forgotten PIN — clearing the app data is the only way to reset it.": "4 xonali PIN tanlang. Readest'ni har ochganingizda uni kiritishingiz kerak bo'ladi. Unutilgan PIN'ni tiklashning iloji yo'q — ilova ma'lumotlarini tozalash uni qayta tiklashning yagona yo'lidir.",
|
||||
"Enter your current PIN, then choose a new 4-digit PIN.": "Joriy PIN'ingizni kiriting, so'ngra yangi 4 xonali PIN tanlang.",
|
||||
"Enter your current PIN to disable the app lock.": "Ilova qulfini o'chirish uchun joriy PIN'ingizni kiriting.",
|
||||
"PIN must be {{length}} digits": "PIN {{length}} xonali bo'lishi kerak",
|
||||
"PINs do not match": "PIN'lar mos kelmaydi",
|
||||
"Current PIN": "Joriy PIN",
|
||||
"New PIN": "Yangi PIN",
|
||||
"Confirm new PIN": "Yangi PIN'ni tasdiqlash"
|
||||
}
|
||||
|
||||
@@ -1316,5 +1316,43 @@
|
||||
"Failed to download file: {{title}}": "Không thể tải về tệp: {{title}}",
|
||||
"Failed to delete cloud copy of the file: {{title}}": "Không thể xóa bản sao đám mây của tệp: {{title}}",
|
||||
"{{percentage}}% used": "Đã dùng {{percentage}}%",
|
||||
"Resets in {{duration}}": "Đặt lại sau {{duration}}"
|
||||
"Resets in {{duration}}": "Đặt lại sau {{duration}}",
|
||||
"Set PIN…": "Đặt mã PIN…",
|
||||
"Require a 4-digit PIN to open Readest": "Yêu cầu mã PIN 4 chữ số để mở Readest",
|
||||
"Change PIN…": "Thay đổi mã PIN…",
|
||||
"Disable PIN…": "Tắt mã PIN…",
|
||||
"Sync passphrase ready": "Cụm mật khẩu đồng bộ đã sẵn sàng",
|
||||
"This permanently deletes the encrypted credentials we sync (e.g., OPDS catalog passwords) on every device. Local copies are preserved. You will need to re-enter the sync passphrase or set a new one. Continue?": "Điều này sẽ xóa vĩnh viễn các thông tin đăng nhập đã mã hóa mà chúng tôi đồng bộ (ví dụ: mật khẩu danh mục OPDS) trên mọi thiết bị. Các bản sao cục bộ được giữ lại. Bạn sẽ cần nhập lại cụm mật khẩu đồng bộ hoặc đặt cụm mới. Tiếp tục?",
|
||||
"Sync passphrase forgotten — all encrypted fields cleared": "Đã quên cụm mật khẩu đồng bộ — tất cả các trường đã mã hóa đã được xóa",
|
||||
"Sync passphrase": "Cụm mật khẩu đồng bộ",
|
||||
"Encrypts sensitive synced fields (like OPDS catalog credentials) before they leave your device. Set one now or wait — it will be requested when needed.": "Mã hóa các trường được đồng bộ nhạy cảm (như thông tin đăng nhập danh mục OPDS) trước khi chúng rời khỏi thiết bị của bạn. Đặt ngay bây giờ hoặc chờ — sẽ được yêu cầu khi cần.",
|
||||
"Set on this account. Will be requested when needed to decrypt synced credentials.": "Đã đặt trên tài khoản này. Sẽ được yêu cầu khi cần để giải mã thông tin đăng nhập đã đồng bộ.",
|
||||
"Set passphrase": "Đặt cụm mật khẩu",
|
||||
"Unlock now": "Mở khóa ngay",
|
||||
"Forgot passphrase": "Quên cụm mật khẩu",
|
||||
"Incorrect PIN": "Mã PIN không đúng",
|
||||
"App locked": "Ứng dụng đã khóa",
|
||||
"Enter your PIN": "Nhập mã PIN của bạn",
|
||||
"Readest is locked. Enter your 4-digit PIN to continue.": "Readest đã bị khóa. Nhập mã PIN 4 chữ số của bạn để tiếp tục.",
|
||||
"PIN code": "Mã PIN",
|
||||
"Forgetting your PIN locks you out of this device. You'll need to clear the app's data to reset it.": "Quên mã PIN sẽ khiến bạn bị khóa khỏi thiết bị này. Bạn sẽ cần xóa dữ liệu của ứng dụng để đặt lại.",
|
||||
"Passphrase must be at least 8 characters": "Cụm mật khẩu phải có ít nhất 8 ký tự",
|
||||
"Passphrases do not match": "Các cụm mật khẩu không khớp",
|
||||
"Set sync passphrase": "Đặt cụm mật khẩu đồng bộ",
|
||||
"Enter sync passphrase": "Nhập cụm mật khẩu đồng bộ",
|
||||
"A sync passphrase encrypts your sensitive fields (like OPDS catalog credentials) before they sync. We never see this passphrase. Pick something memorable — there is no recovery without it.": "Cụm mật khẩu đồng bộ mã hóa các trường nhạy cảm của bạn (như thông tin đăng nhập danh mục OPDS) trước khi đồng bộ. Chúng tôi không bao giờ thấy cụm mật khẩu này. Chọn một cụm dễ nhớ — không thể khôi phục nếu thiếu nó.",
|
||||
"Enter the sync passphrase you set on another device to decrypt your synced credentials.": "Nhập cụm mật khẩu đồng bộ mà bạn đã đặt trên thiết bị khác để giải mã thông tin đăng nhập đã đồng bộ.",
|
||||
"Confirm passphrase": "Xác nhận cụm mật khẩu",
|
||||
"Unlock": "Mở khóa",
|
||||
"Set PIN": "Đặt mã PIN",
|
||||
"Change PIN": "Thay đổi mã PIN",
|
||||
"Disable PIN": "Tắt mã PIN",
|
||||
"Pick a 4-digit PIN. You will need to enter it every time you open Readest. There is no way to recover a forgotten PIN — clearing the app data is the only way to reset it.": "Chọn mã PIN 4 chữ số. Bạn sẽ cần nhập mã này mỗi khi mở Readest. Không có cách nào khôi phục mã PIN đã quên — xóa dữ liệu ứng dụng là cách duy nhất để đặt lại.",
|
||||
"Enter your current PIN, then choose a new 4-digit PIN.": "Nhập mã PIN hiện tại của bạn, sau đó chọn mã PIN 4 chữ số mới.",
|
||||
"Enter your current PIN to disable the app lock.": "Nhập mã PIN hiện tại của bạn để tắt khóa ứng dụng.",
|
||||
"PIN must be {{length}} digits": "Mã PIN phải có {{length}} chữ số",
|
||||
"PINs do not match": "Mã PIN không khớp",
|
||||
"Current PIN": "Mã PIN hiện tại",
|
||||
"New PIN": "Mã PIN mới",
|
||||
"Confirm new PIN": "Xác nhận mã PIN mới"
|
||||
}
|
||||
|
||||
@@ -1316,5 +1316,43 @@
|
||||
"Failed to download file: {{title}}": "下载文件失败:{{title}}",
|
||||
"Failed to delete cloud copy of the file: {{title}}": "未能删除文件云副本:{{title}}",
|
||||
"{{percentage}}% used": "已使用 {{percentage}}%",
|
||||
"Resets in {{duration}}": "{{duration}}后重置"
|
||||
"Resets in {{duration}}": "{{duration}}后重置",
|
||||
"Set PIN…": "设置 PIN 码…",
|
||||
"Require a 4-digit PIN to open Readest": "打开 Readest 时需要输入 4 位 PIN 码",
|
||||
"Change PIN…": "更改 PIN 码…",
|
||||
"Disable PIN…": "停用 PIN 码…",
|
||||
"Sync passphrase ready": "同步密语已就绪",
|
||||
"This permanently deletes the encrypted credentials we sync (e.g., OPDS catalog passwords) on every device. Local copies are preserved. You will need to re-enter the sync passphrase or set a new one. Continue?": "这将在每台设备上永久删除我们同步的加密凭据(例如 OPDS 目录密码)。本地副本会保留。您需要重新输入同步密语或设置新的密语。是否继续?",
|
||||
"Sync passphrase forgotten — all encrypted fields cleared": "已忘记同步密语 — 所有加密字段已清除",
|
||||
"Sync passphrase": "同步密语",
|
||||
"Encrypts sensitive synced fields (like OPDS catalog credentials) before they leave your device. Set one now or wait — it will be requested when needed.": "在敏感的同步字段(例如 OPDS 目录凭据)离开您的设备之前对其进行加密。立即设置或稍后再设 — 需要时会请求输入。",
|
||||
"Set on this account. Will be requested when needed to decrypt synced credentials.": "已在此账户上设置。需要解密同步凭据时将请求输入。",
|
||||
"Set passphrase": "设置密语",
|
||||
"Unlock now": "立即解锁",
|
||||
"Forgot passphrase": "忘记密语",
|
||||
"Incorrect PIN": "PIN 码错误",
|
||||
"App locked": "应用已锁定",
|
||||
"Enter your PIN": "请输入 PIN 码",
|
||||
"Readest is locked. Enter your 4-digit PIN to continue.": "Readest 已锁定。请输入 4 位 PIN 码以继续。",
|
||||
"PIN code": "PIN 码",
|
||||
"Forgetting your PIN locks you out of this device. You'll need to clear the app's data to reset it.": "忘记 PIN 码将使您无法在此设备上使用 Readest。您需要清除应用数据才能重置。",
|
||||
"Passphrase must be at least 8 characters": "密语至少需要 8 个字符",
|
||||
"Passphrases do not match": "两次输入的密语不一致",
|
||||
"Set sync passphrase": "设置同步密语",
|
||||
"Enter sync passphrase": "输入同步密语",
|
||||
"A sync passphrase encrypts your sensitive fields (like OPDS catalog credentials) before they sync. We never see this passphrase. Pick something memorable — there is no recovery without it.": "同步密语会在您的敏感字段(例如 OPDS 目录凭据)同步之前对其加密。我们从不查看此密语。请选择易记的内容 — 一旦遗失将无法恢复。",
|
||||
"Enter the sync passphrase you set on another device to decrypt your synced credentials.": "请输入您在其他设备上设置的同步密语,以解密已同步的凭据。",
|
||||
"Confirm passphrase": "确认密语",
|
||||
"Unlock": "解锁",
|
||||
"Set PIN": "设置 PIN 码",
|
||||
"Change PIN": "更改 PIN 码",
|
||||
"Disable PIN": "停用 PIN 码",
|
||||
"Pick a 4-digit PIN. You will need to enter it every time you open Readest. There is no way to recover a forgotten PIN — clearing the app data is the only way to reset it.": "请选择一个 4 位 PIN 码。每次打开 Readest 时都需要输入。无法恢复忘记的 PIN 码 — 清除应用数据是重置的唯一方法。",
|
||||
"Enter your current PIN, then choose a new 4-digit PIN.": "请输入当前 PIN 码,然后选择新的 4 位 PIN 码。",
|
||||
"Enter your current PIN to disable the app lock.": "请输入当前 PIN 码以停用应用锁。",
|
||||
"PIN must be {{length}} digits": "PIN 码必须为 {{length}} 位",
|
||||
"PINs do not match": "PIN 码不匹配",
|
||||
"Current PIN": "当前 PIN 码",
|
||||
"New PIN": "新 PIN 码",
|
||||
"Confirm new PIN": "确认新 PIN 码"
|
||||
}
|
||||
|
||||
@@ -1316,5 +1316,43 @@
|
||||
"Failed to download file: {{title}}": "檔案下載失敗:{{title}}",
|
||||
"Failed to delete cloud copy of the file: {{title}}": "未能刪除檔案的雲端副本:{{title}}",
|
||||
"{{percentage}}% used": "已使用 {{percentage}}%",
|
||||
"Resets in {{duration}}": "{{duration}}後重設"
|
||||
"Resets in {{duration}}": "{{duration}}後重設",
|
||||
"Set PIN…": "設定 PIN 碼…",
|
||||
"Require a 4-digit PIN to open Readest": "開啟 Readest 時需要輸入 4 位 PIN 碼",
|
||||
"Change PIN…": "變更 PIN 碼…",
|
||||
"Disable PIN…": "停用 PIN 碼…",
|
||||
"Sync passphrase ready": "同步密語已就緒",
|
||||
"This permanently deletes the encrypted credentials we sync (e.g., OPDS catalog passwords) on every device. Local copies are preserved. You will need to re-enter the sync passphrase or set a new one. Continue?": "這將在每台裝置上永久刪除我們同步的加密憑證(例如 OPDS 目錄密碼)。本機副本會保留。您需要重新輸入同步密語或設定新的密語。是否繼續?",
|
||||
"Sync passphrase forgotten — all encrypted fields cleared": "已忘記同步密語 — 所有加密欄位已清除",
|
||||
"Sync passphrase": "同步密語",
|
||||
"Encrypts sensitive synced fields (like OPDS catalog credentials) before they leave your device. Set one now or wait — it will be requested when needed.": "在敏感的同步欄位(例如 OPDS 目錄憑證)離開您的裝置之前對其進行加密。立即設定或稍後再設 — 需要時會請求輸入。",
|
||||
"Set on this account. Will be requested when needed to decrypt synced credentials.": "已在此帳戶上設定。需要解密同步憑證時將請求輸入。",
|
||||
"Set passphrase": "設定密語",
|
||||
"Unlock now": "立即解鎖",
|
||||
"Forgot passphrase": "忘記密語",
|
||||
"Incorrect PIN": "PIN 碼錯誤",
|
||||
"App locked": "應用程式已鎖定",
|
||||
"Enter your PIN": "請輸入 PIN 碼",
|
||||
"Readest is locked. Enter your 4-digit PIN to continue.": "Readest 已鎖定。請輸入 4 位 PIN 碼以繼續。",
|
||||
"PIN code": "PIN 碼",
|
||||
"Forgetting your PIN locks you out of this device. You'll need to clear the app's data to reset it.": "忘記 PIN 碼將使您無法在此裝置上使用 Readest。您需要清除應用程式資料才能重設。",
|
||||
"Passphrase must be at least 8 characters": "密語至少需要 8 個字元",
|
||||
"Passphrases do not match": "兩次輸入的密語不一致",
|
||||
"Set sync passphrase": "設定同步密語",
|
||||
"Enter sync passphrase": "輸入同步密語",
|
||||
"A sync passphrase encrypts your sensitive fields (like OPDS catalog credentials) before they sync. We never see this passphrase. Pick something memorable — there is no recovery without it.": "同步密語會在您的敏感欄位(例如 OPDS 目錄憑證)同步之前對其加密。我們從不查看此密語。請選擇易記的內容 — 一旦遺失將無法復原。",
|
||||
"Enter the sync passphrase you set on another device to decrypt your synced credentials.": "請輸入您在其他裝置上設定的同步密語,以解密已同步的憑證。",
|
||||
"Confirm passphrase": "確認密語",
|
||||
"Unlock": "解鎖",
|
||||
"Set PIN": "設定 PIN 碼",
|
||||
"Change PIN": "變更 PIN 碼",
|
||||
"Disable PIN": "停用 PIN 碼",
|
||||
"Pick a 4-digit PIN. You will need to enter it every time you open Readest. There is no way to recover a forgotten PIN — clearing the app data is the only way to reset it.": "請選擇一個 4 位 PIN 碼。每次開啟 Readest 時都需要輸入。無法復原忘記的 PIN 碼 — 清除應用程式資料是重設的唯一方法。",
|
||||
"Enter your current PIN, then choose a new 4-digit PIN.": "請輸入目前 PIN 碼,然後選擇新的 4 位 PIN 碼。",
|
||||
"Enter your current PIN to disable the app lock.": "請輸入目前 PIN 碼以停用應用程式鎖定。",
|
||||
"PIN must be {{length}} digits": "PIN 碼必須為 {{length}} 位",
|
||||
"PINs do not match": "PIN 碼不相符",
|
||||
"Current PIN": "目前 PIN 碼",
|
||||
"New PIN": "新 PIN 碼",
|
||||
"Confirm new PIN": "確認新 PIN 碼"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
import { describe, expect, test } from 'vitest';
|
||||
import {
|
||||
PIN_LENGTH,
|
||||
PIN_SALT_BYTES,
|
||||
generatePinSalt,
|
||||
hashPin,
|
||||
isValidPin,
|
||||
verifyPin,
|
||||
} from '@/libs/crypto/applock';
|
||||
|
||||
const HEX_RE = /^[0-9a-f]+$/;
|
||||
|
||||
describe('app-lock PIN crypto', () => {
|
||||
test('generatePinSalt returns hex of expected length', () => {
|
||||
const salt = generatePinSalt();
|
||||
expect(salt).toMatch(HEX_RE);
|
||||
expect(salt.length).toBe(PIN_SALT_BYTES * 2);
|
||||
});
|
||||
|
||||
test('generatePinSalt returns a fresh salt each call', () => {
|
||||
const seen = new Set<string>();
|
||||
for (let i = 0; i < 20; i++) seen.add(generatePinSalt());
|
||||
expect(seen.size).toBe(20);
|
||||
});
|
||||
|
||||
test('hashPin is deterministic for the same pin and salt', async () => {
|
||||
const salt = generatePinSalt();
|
||||
const a = await hashPin('1234', salt);
|
||||
const b = await hashPin('1234', salt);
|
||||
expect(a).toBe(b);
|
||||
});
|
||||
|
||||
test('hashPin output is hex of fixed length (32 bytes / 64 chars)', async () => {
|
||||
const hash = await hashPin('0000', generatePinSalt());
|
||||
expect(hash).toMatch(HEX_RE);
|
||||
expect(hash.length).toBe(64);
|
||||
});
|
||||
|
||||
test('hashPin produces different output for different pins', async () => {
|
||||
const salt = generatePinSalt();
|
||||
const a = await hashPin('1234', salt);
|
||||
const b = await hashPin('1235', salt);
|
||||
expect(a).not.toBe(b);
|
||||
});
|
||||
|
||||
test('hashPin produces different output for different salts', async () => {
|
||||
const a = await hashPin('1234', generatePinSalt());
|
||||
const b = await hashPin('1234', generatePinSalt());
|
||||
expect(a).not.toBe(b);
|
||||
});
|
||||
|
||||
test('verifyPin accepts the correct pin', async () => {
|
||||
const salt = generatePinSalt();
|
||||
const hash = await hashPin('4242', salt);
|
||||
expect(await verifyPin('4242', salt, hash)).toBe(true);
|
||||
});
|
||||
|
||||
test('verifyPin rejects an incorrect pin', async () => {
|
||||
const salt = generatePinSalt();
|
||||
const hash = await hashPin('4242', salt);
|
||||
expect(await verifyPin('4243', salt, hash)).toBe(false);
|
||||
});
|
||||
|
||||
test('verifyPin rejects a hash that was generated with a different salt', async () => {
|
||||
const saltA = generatePinSalt();
|
||||
const saltB = generatePinSalt();
|
||||
const hash = await hashPin('1234', saltA);
|
||||
expect(await verifyPin('1234', saltB, hash)).toBe(false);
|
||||
});
|
||||
|
||||
test('isValidPin enforces 4 ASCII digits', () => {
|
||||
expect(isValidPin('1234')).toBe(true);
|
||||
expect(isValidPin('0000')).toBe(true);
|
||||
expect(isValidPin('123')).toBe(false);
|
||||
expect(isValidPin('12345')).toBe(false);
|
||||
expect(isValidPin('12a4')).toBe(false);
|
||||
expect(isValidPin('')).toBe(false);
|
||||
expect(isValidPin(' 1234')).toBe(false);
|
||||
expect(isValidPin(`${PIN_LENGTH}`.padStart(PIN_LENGTH + 1, '0'))).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -32,6 +32,7 @@ import UserAvatar from '@/components/UserAvatar';
|
||||
import MenuItem from '@/components/MenuItem';
|
||||
import Quota from '@/components/Quota';
|
||||
import Menu from '@/components/Menu';
|
||||
import { type AppLockDialogMode, useAppLockStore } from '@/store/appLockStore';
|
||||
|
||||
interface SettingsMenuProps {
|
||||
onPullLibrary: (fullRefresh?: boolean, verbose?: boolean) => void;
|
||||
@@ -68,6 +69,13 @@ const SettingsMenu: React.FC<SettingsMenuProps> = ({ onPullLibrary, setIsDropdow
|
||||
|
||||
const [isRefreshingMetadata, setIsRefreshingMetadata] = useState(false);
|
||||
const [refreshMetadataProgress, setRefreshMetadataProgress] = useState('');
|
||||
const { openDialog: openAppLockDialogInStore } = useAppLockStore();
|
||||
const isPinEnabled = !!settings.pinCodeEnabled;
|
||||
|
||||
const openAppLockDialog = (mode: AppLockDialogMode) => {
|
||||
openAppLockDialogInStore(mode);
|
||||
setIsDropdownOpen?.(false);
|
||||
};
|
||||
const { isSyncing, setLibrary } = useLibraryStore();
|
||||
const { stats, hasActiveTransfers, setIsTransferQueueOpen } = useTransferQueue();
|
||||
|
||||
@@ -412,6 +420,19 @@ const SettingsMenu: React.FC<SettingsMenuProps> = ({ onPullLibrary, setIsDropdow
|
||||
onClick={handleRefreshMetadata}
|
||||
disabled={isRefreshingMetadata}
|
||||
/>
|
||||
{!isPinEnabled && (
|
||||
<MenuItem
|
||||
label={_('Set PIN…')}
|
||||
tooltip={_('Require a 4-digit PIN to open Readest')}
|
||||
onClick={() => openAppLockDialog('set')}
|
||||
/>
|
||||
)}
|
||||
{isPinEnabled && (
|
||||
<MenuItem label={_('Change PIN…')} onClick={() => openAppLockDialog('change')} />
|
||||
)}
|
||||
{isPinEnabled && (
|
||||
<MenuItem label={_('Disable PIN…')} onClick={() => openAppLockDialog('disable')} />
|
||||
)}
|
||||
{appService?.isAndroidApp && appService?.distChannel !== 'playstore' && (
|
||||
<MenuItem
|
||||
label={_('Save Book Cover')}
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
'use client';
|
||||
|
||||
import clsx from 'clsx';
|
||||
import { useRef, useState } from 'react';
|
||||
|
||||
import PinInput from '@/components/PinInput';
|
||||
import { PIN_LENGTH, verifyPin } from '@/libs/crypto/applock';
|
||||
import { useAppLockStore } from '@/store/appLockStore';
|
||||
import { useTranslation } from '@/hooks/useTranslation';
|
||||
|
||||
export default function AppLockScreen() {
|
||||
const _ = useTranslation();
|
||||
const { pinHash, pinSalt, unlock } = useAppLockStore();
|
||||
const [pin, setPin] = useState('');
|
||||
const [error, setError] = useState('');
|
||||
const [shaking, setShaking] = useState(false);
|
||||
// Avoid React state for the in-flight guard — `setVerifying(true)`
|
||||
// would re-trigger the effect, the cleanup would set `cancelled=true`,
|
||||
// and the resolve handler would short-circuit before clearing the
|
||||
// failed PIN. A ref keeps the guard outside the render cycle.
|
||||
const verifyingRef = useRef(false);
|
||||
|
||||
const handleChange = async (next: string) => {
|
||||
setPin(next);
|
||||
if (error) setError('');
|
||||
if (next.length !== PIN_LENGTH || verifyingRef.current) return;
|
||||
if (!pinHash || !pinSalt) {
|
||||
// Settings flag is enabled but the hash/salt are missing — treat
|
||||
// as a corrupted-config bypass so the user isn't locked out
|
||||
// forever. This should be unreachable through normal flows.
|
||||
unlock();
|
||||
return;
|
||||
}
|
||||
verifyingRef.current = true;
|
||||
try {
|
||||
const ok = await verifyPin(next, pinSalt, pinHash);
|
||||
if (ok) {
|
||||
unlock();
|
||||
} else {
|
||||
setError(_('Incorrect PIN'));
|
||||
setPin('');
|
||||
setShaking(true);
|
||||
window.setTimeout(() => setShaking(false), 400);
|
||||
}
|
||||
} finally {
|
||||
verifyingRef.current = false;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className='bg-base-100 fixed inset-0 z-[200] flex flex-col items-center justify-center px-6'
|
||||
role='dialog'
|
||||
aria-modal='true'
|
||||
aria-label={_('App locked')}
|
||||
>
|
||||
<div className='flex max-w-sm flex-col items-center text-center'>
|
||||
<h1 className='text-base-content mb-2 text-xl font-semibold tracking-tight'>
|
||||
{_('Enter your PIN')}
|
||||
</h1>
|
||||
<p className='text-base-content/60 mb-8 text-sm leading-relaxed'>
|
||||
{_('Readest is locked. Enter your 4-digit PIN to continue.')}
|
||||
</p>
|
||||
|
||||
<PinInput
|
||||
value={pin}
|
||||
onChange={handleChange}
|
||||
ariaLabel={_('PIN code')}
|
||||
stickyFocus
|
||||
shake={shaking}
|
||||
/>
|
||||
|
||||
<p
|
||||
className={clsx(
|
||||
'text-error mt-4 h-5 text-sm transition-opacity',
|
||||
error ? 'opacity-100' : 'opacity-0',
|
||||
)}
|
||||
aria-live='polite'
|
||||
>
|
||||
{error || ' '}
|
||||
</p>
|
||||
|
||||
<p className='text-base-content/40 mt-10 text-xs leading-relaxed'>
|
||||
{_(
|
||||
"Forgetting your PIN locks you out of this device. You'll need to clear the app's data to reset it.",
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
'use client';
|
||||
|
||||
import clsx from 'clsx';
|
||||
import { forwardRef, useEffect, useImperativeHandle, useRef } from 'react';
|
||||
|
||||
import { PIN_LENGTH } from '@/libs/crypto/applock';
|
||||
|
||||
interface PinInputProps {
|
||||
value: string;
|
||||
onChange: (next: string) => void;
|
||||
autoFocus?: boolean;
|
||||
shake?: boolean;
|
||||
disabled?: boolean;
|
||||
ariaLabel: string;
|
||||
/**
|
||||
* When true, the component keeps focus pinned to the hidden input —
|
||||
* used by the full-screen lock screen so the on-screen keyboard
|
||||
* stays available even after taps elsewhere on the page.
|
||||
*/
|
||||
stickyFocus?: boolean;
|
||||
/** Form-level autocomplete hint. */
|
||||
autoComplete?: 'one-time-code' | 'current-password' | 'new-password';
|
||||
}
|
||||
|
||||
export interface PinInputHandle {
|
||||
focus: () => void;
|
||||
}
|
||||
|
||||
const PinDot = ({ filled }: { filled: boolean }) => (
|
||||
<div
|
||||
className={clsx(
|
||||
'eink-bordered flex h-12 w-10 items-center justify-center rounded-lg border',
|
||||
'border-base-content/20 bg-base-200/60',
|
||||
filled && 'border-base-content/40',
|
||||
)}
|
||||
>
|
||||
<span
|
||||
className={clsx(
|
||||
'inline-block h-3 w-3 rounded-full transition-opacity',
|
||||
filled ? 'bg-base-content opacity-100' : 'opacity-0',
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
const PinInput = forwardRef<PinInputHandle, PinInputProps>(function PinInput(
|
||||
{
|
||||
value,
|
||||
onChange,
|
||||
autoFocus,
|
||||
shake,
|
||||
disabled,
|
||||
ariaLabel,
|
||||
stickyFocus,
|
||||
autoComplete = 'one-time-code',
|
||||
},
|
||||
ref,
|
||||
) {
|
||||
const inputRef = useRef<HTMLInputElement | null>(null);
|
||||
useImperativeHandle(ref, () => ({ focus: () => inputRef.current?.focus() }));
|
||||
|
||||
useEffect(() => {
|
||||
if (autoFocus) {
|
||||
const t = window.setTimeout(() => inputRef.current?.focus(), 0);
|
||||
return () => window.clearTimeout(t);
|
||||
}
|
||||
return undefined;
|
||||
}, [autoFocus]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!stickyFocus) return;
|
||||
const focus = () => inputRef.current?.focus();
|
||||
focus();
|
||||
const t = window.setInterval(focus, 200);
|
||||
return () => window.clearInterval(t);
|
||||
}, [stickyFocus]);
|
||||
|
||||
const handleClick = () => inputRef.current?.focus();
|
||||
|
||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const next = e.target.value.replace(/\D/g, '').slice(0, PIN_LENGTH);
|
||||
onChange(next);
|
||||
};
|
||||
|
||||
return (
|
||||
<label className='relative block w-fit cursor-pointer' onClick={handleClick}>
|
||||
<span className='sr-only'>{ariaLabel}</span>
|
||||
<input
|
||||
ref={inputRef}
|
||||
type='password'
|
||||
inputMode='numeric'
|
||||
pattern='[0-9]*'
|
||||
maxLength={PIN_LENGTH}
|
||||
value={value}
|
||||
onChange={handleChange}
|
||||
disabled={disabled}
|
||||
autoComplete={autoComplete}
|
||||
aria-label={ariaLabel}
|
||||
className='absolute inset-0 z-10 cursor-pointer opacity-0'
|
||||
/>
|
||||
<div className={clsx('flex gap-3', shake && 'animate-pin-shake')}>
|
||||
{Array.from({ length: PIN_LENGTH }).map((_dot, i) => (
|
||||
<PinDot key={i} filled={i < value.length} />
|
||||
))}
|
||||
</div>
|
||||
</label>
|
||||
);
|
||||
});
|
||||
|
||||
export default PinInput;
|
||||
@@ -19,15 +19,23 @@ import { getDirFromUILanguage } from '@/utils/rtl';
|
||||
import { DropdownProvider } from '@/context/DropdownContext';
|
||||
import { CommandPaletteProvider, CommandPalette } from '@/components/command-palette';
|
||||
import AtmosphereOverlay from '@/components/AtmosphereOverlay';
|
||||
import AppLockScreen from '@/components/AppLockScreen';
|
||||
import AppLockDialog from '@/components/settings/AppLockDialog';
|
||||
import PassphrasePrompt from '@/components/PassphrasePrompt';
|
||||
import { upgradeToKeychainIfAvailable } from '@/libs/crypto/passphrase';
|
||||
import { cryptoSession } from '@/libs/crypto/session';
|
||||
import { useAppLockStore } from '@/store/appLockStore';
|
||||
|
||||
const Providers = ({ children }: { children: React.ReactNode }) => {
|
||||
const { envConfig, appService } = useEnv();
|
||||
const { applyUILanguage } = useSettingsStore();
|
||||
const { applyBackgroundTexture } = useBackgroundTexture();
|
||||
const { applyEinkMode } = useEinkMode();
|
||||
const {
|
||||
isInitialized: isLockInitialized,
|
||||
isUnlocked,
|
||||
initialize: initializeAppLock,
|
||||
} = useAppLockStore();
|
||||
const iconSize = useDefaultIconSize();
|
||||
useSafeAreaInsets(); // Initialize safe area insets
|
||||
|
||||
@@ -62,9 +70,24 @@ const Providers = ({ children }: { children: React.ReactNode }) => {
|
||||
if (globalViewSettings.isEink) {
|
||||
applyEinkMode(true);
|
||||
}
|
||||
// Initialize the app-lock gate from on-disk settings. Until
|
||||
// this runs, the gate renders nothing — guarantees the
|
||||
// library can't flash on screen before the lock screen does.
|
||||
initializeAppLock({
|
||||
enabled: !!settings.pinCodeEnabled,
|
||||
hash: settings.pinCodeHash,
|
||||
salt: settings.pinCodeSalt,
|
||||
});
|
||||
});
|
||||
}
|
||||
}, [envConfig, appService, applyUILanguage, applyBackgroundTexture, applyEinkMode]);
|
||||
}, [
|
||||
envConfig,
|
||||
appService,
|
||||
applyUILanguage,
|
||||
applyBackgroundTexture,
|
||||
applyEinkMode,
|
||||
initializeAppLock,
|
||||
]);
|
||||
|
||||
// Sync-passphrase boot path: upgrade the passphrase store from
|
||||
// ephemeral to OS keychain on Tauri (probe is async — must run after
|
||||
@@ -81,6 +104,13 @@ const Providers = ({ children }: { children: React.ReactNode }) => {
|
||||
// Make sure appService is available in all children components
|
||||
if (!appService) return;
|
||||
|
||||
// App-lock gate. While the lock store is uninitialized we render
|
||||
// nothing — without this guard the library would flash on screen
|
||||
// for a few hundred ms before `loadSettings` resolved and let the
|
||||
// lock store decide whether to lock.
|
||||
const showAppLockScreen = isLockInitialized && !isUnlocked;
|
||||
const appShellHidden = !isLockInitialized || !isUnlocked;
|
||||
|
||||
return (
|
||||
<CSPostHogProvider>
|
||||
<AuthProvider>
|
||||
@@ -88,10 +118,17 @@ const Providers = ({ children }: { children: React.ReactNode }) => {
|
||||
<SyncProvider>
|
||||
<DropdownProvider>
|
||||
<CommandPaletteProvider>
|
||||
{children}
|
||||
<CommandPalette />
|
||||
<AtmosphereOverlay />
|
||||
<PassphrasePrompt />
|
||||
<div
|
||||
aria-hidden={appShellHidden}
|
||||
style={appShellHidden ? { display: 'none' } : undefined}
|
||||
>
|
||||
{children}
|
||||
<CommandPalette />
|
||||
<AtmosphereOverlay />
|
||||
<PassphrasePrompt />
|
||||
</div>
|
||||
<AppLockDialog />
|
||||
{showAppLockScreen && <AppLockScreen />}
|
||||
</CommandPaletteProvider>
|
||||
</DropdownProvider>
|
||||
</SyncProvider>
|
||||
|
||||
@@ -0,0 +1,274 @@
|
||||
'use client';
|
||||
|
||||
import clsx from 'clsx';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
|
||||
import ModalPortal from '@/components/ModalPortal';
|
||||
import PinInput, { type PinInputHandle } from '@/components/PinInput';
|
||||
import { useEnv } from '@/context/EnvContext';
|
||||
import { useTranslation } from '@/hooks/useTranslation';
|
||||
import { saveSysSettings } from '@/helpers/settings';
|
||||
import { PIN_LENGTH, generatePinSalt, hashPin, isValidPin, verifyPin } from '@/libs/crypto/applock';
|
||||
import { useAppLockStore } from '@/store/appLockStore';
|
||||
import { useSettingsStore } from '@/store/settingsStore';
|
||||
|
||||
const fieldLabelClass = 'text-base-content/70 text-xs font-medium tracking-wide';
|
||||
|
||||
/**
|
||||
* Always mounted (at Providers level). Reads `dialogMode` from
|
||||
* `useAppLockStore`; renders nothing when null. Dialog state lives in
|
||||
* the store rather than the SettingsMenu component because that
|
||||
* component unmounts the moment its dropdown closes — local state
|
||||
* would never make it to a render.
|
||||
*/
|
||||
export default function AppLockDialog() {
|
||||
const _ = useTranslation();
|
||||
const { envConfig } = useEnv();
|
||||
const { settings } = useSettingsStore();
|
||||
const {
|
||||
pinHash,
|
||||
pinSalt,
|
||||
setPin: setStorePin,
|
||||
clearPin,
|
||||
dialogMode,
|
||||
closeDialog,
|
||||
} = useAppLockStore();
|
||||
const mode = dialogMode;
|
||||
|
||||
const [currentPin, setCurrentPin] = useState('');
|
||||
const [newPin, setNewPin] = useState('');
|
||||
const [confirmPin, setConfirmPin] = useState('');
|
||||
const [error, setError] = useState('');
|
||||
const [busy, setBusy] = useState(false);
|
||||
|
||||
const currentPinRef = useRef<PinInputHandle | null>(null);
|
||||
const newPinRef = useRef<PinInputHandle | null>(null);
|
||||
const confirmPinRef = useRef<PinInputHandle | null>(null);
|
||||
|
||||
// Reset every field on open so a previous attempt's digits never
|
||||
// leak into the next session.
|
||||
useEffect(() => {
|
||||
if (!mode) return;
|
||||
setCurrentPin('');
|
||||
setNewPin('');
|
||||
setConfirmPin('');
|
||||
setError('');
|
||||
}, [mode]);
|
||||
|
||||
// Auto-advance: as soon as a field reaches PIN_LENGTH, move focus
|
||||
// to the next field. Saves the user from tabbing.
|
||||
const advanceFromCurrent = (next: string) => {
|
||||
setCurrentPin(next);
|
||||
if (error) setError('');
|
||||
if (next.length === PIN_LENGTH && (mode === 'change' || mode === 'set')) {
|
||||
newPinRef.current?.focus();
|
||||
}
|
||||
};
|
||||
const advanceFromNew = (next: string) => {
|
||||
setNewPin(next);
|
||||
if (error) setError('');
|
||||
if (next.length === PIN_LENGTH) confirmPinRef.current?.focus();
|
||||
};
|
||||
const onConfirmChange = (next: string) => {
|
||||
setConfirmPin(next);
|
||||
if (error) setError('');
|
||||
};
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (busy || !mode) return;
|
||||
setError('');
|
||||
|
||||
if (mode === 'set') {
|
||||
if (!isValidPin(newPin)) {
|
||||
setError(_('PIN must be {{length}} digits', { length: PIN_LENGTH }));
|
||||
return;
|
||||
}
|
||||
if (newPin !== confirmPin) {
|
||||
setError(_('PINs do not match'));
|
||||
return;
|
||||
}
|
||||
setBusy(true);
|
||||
try {
|
||||
const salt = generatePinSalt();
|
||||
const hash = await hashPin(newPin, salt);
|
||||
await saveSysSettings(envConfig, 'pinCodeSalt', salt);
|
||||
await saveSysSettings(envConfig, 'pinCodeHash', hash);
|
||||
await saveSysSettings(envConfig, 'pinCodeEnabled', true);
|
||||
setStorePin(hash, salt);
|
||||
closeDialog();
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode === 'change') {
|
||||
if (!pinHash || !pinSalt) {
|
||||
closeDialog();
|
||||
return;
|
||||
}
|
||||
if (!isValidPin(currentPin)) {
|
||||
setError(_('PIN must be {{length}} digits', { length: PIN_LENGTH }));
|
||||
return;
|
||||
}
|
||||
setBusy(true);
|
||||
try {
|
||||
const ok = await verifyPin(currentPin, pinSalt, pinHash);
|
||||
if (!ok) {
|
||||
setError(_('Incorrect PIN'));
|
||||
setCurrentPin('');
|
||||
currentPinRef.current?.focus();
|
||||
return;
|
||||
}
|
||||
if (!isValidPin(newPin)) {
|
||||
setError(_('PIN must be {{length}} digits', { length: PIN_LENGTH }));
|
||||
return;
|
||||
}
|
||||
if (newPin !== confirmPin) {
|
||||
setError(_('PINs do not match'));
|
||||
return;
|
||||
}
|
||||
const salt = generatePinSalt();
|
||||
const hash = await hashPin(newPin, salt);
|
||||
await saveSysSettings(envConfig, 'pinCodeSalt', salt);
|
||||
await saveSysSettings(envConfig, 'pinCodeHash', hash);
|
||||
setStorePin(hash, salt);
|
||||
closeDialog();
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// mode === 'disable'
|
||||
if (!pinHash || !pinSalt) {
|
||||
closeDialog();
|
||||
return;
|
||||
}
|
||||
if (!isValidPin(currentPin)) {
|
||||
setError(_('PIN must be {{length}} digits', { length: PIN_LENGTH }));
|
||||
return;
|
||||
}
|
||||
setBusy(true);
|
||||
try {
|
||||
const ok = await verifyPin(currentPin, pinSalt, pinHash);
|
||||
if (!ok) {
|
||||
setError(_('Incorrect PIN'));
|
||||
setCurrentPin('');
|
||||
currentPinRef.current?.focus();
|
||||
return;
|
||||
}
|
||||
await saveSysSettings(envConfig, 'pinCodeEnabled', false);
|
||||
await saveSysSettings(envConfig, 'pinCodeHash', undefined);
|
||||
await saveSysSettings(envConfig, 'pinCodeSalt', undefined);
|
||||
clearPin();
|
||||
closeDialog();
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
};
|
||||
|
||||
if (!mode) return null;
|
||||
// Defensive: if `pinCodeEnabled` is somehow stale relative to the
|
||||
// mode the caller asked for, close rather than crash.
|
||||
if (mode !== 'set' && !settings.pinCodeEnabled) {
|
||||
closeDialog();
|
||||
return null;
|
||||
}
|
||||
|
||||
const title =
|
||||
mode === 'set' ? _('Set PIN') : mode === 'change' ? _('Change PIN') : _('Disable PIN');
|
||||
|
||||
const description =
|
||||
mode === 'set'
|
||||
? _(
|
||||
'Pick a 4-digit PIN. You will need to enter it every time you open Readest. There is no way to recover a forgotten PIN — clearing the app data is the only way to reset it.',
|
||||
)
|
||||
: mode === 'change'
|
||||
? _('Enter your current PIN, then choose a new 4-digit PIN.')
|
||||
: _('Enter your current PIN to disable the app lock.');
|
||||
|
||||
return (
|
||||
<ModalPortal>
|
||||
<dialog className='modal modal-open'>
|
||||
<div className='modal-box bg-base-200 max-w-md rounded-2xl p-6 shadow-2xl'>
|
||||
<h3 className='mb-1.5 text-lg font-semibold tracking-tight'>{title}</h3>
|
||||
<p className='text-base-content/60 mb-5 text-sm leading-relaxed'>{description}</p>
|
||||
<form onSubmit={handleSubmit} className='flex flex-col gap-4'>
|
||||
{(mode === 'change' || mode === 'disable') && (
|
||||
<div className='flex flex-col items-center gap-2'>
|
||||
<span className={fieldLabelClass}>{_('Current PIN')}</span>
|
||||
<PinInput
|
||||
ref={currentPinRef}
|
||||
value={currentPin}
|
||||
onChange={advanceFromCurrent}
|
||||
ariaLabel={_('Current PIN')}
|
||||
autoFocus
|
||||
autoComplete='current-password'
|
||||
disabled={busy}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{(mode === 'set' || mode === 'change') && (
|
||||
<>
|
||||
<div className='flex flex-col items-center gap-2'>
|
||||
<span className={fieldLabelClass}>{_('New PIN')}</span>
|
||||
<PinInput
|
||||
ref={newPinRef}
|
||||
value={newPin}
|
||||
onChange={advanceFromNew}
|
||||
ariaLabel={_('New PIN')}
|
||||
autoFocus={mode === 'set'}
|
||||
autoComplete='new-password'
|
||||
disabled={busy}
|
||||
/>
|
||||
</div>
|
||||
<div className='flex flex-col items-center gap-2'>
|
||||
<span className={fieldLabelClass}>{_('Confirm new PIN')}</span>
|
||||
<PinInput
|
||||
ref={confirmPinRef}
|
||||
value={confirmPin}
|
||||
onChange={onConfirmChange}
|
||||
ariaLabel={_('Confirm new PIN')}
|
||||
autoComplete='new-password'
|
||||
disabled={busy}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<p
|
||||
className={clsx(
|
||||
'text-error h-4 text-center text-xs transition-opacity',
|
||||
error ? 'opacity-100' : 'opacity-0',
|
||||
)}
|
||||
aria-live='polite'
|
||||
>
|
||||
{error || ' '}
|
||||
</p>
|
||||
<div className='flex justify-end gap-2'>
|
||||
<button
|
||||
type='button'
|
||||
onClick={closeDialog}
|
||||
disabled={busy}
|
||||
className='eink-bordered hover:bg-base-300/70 rounded-lg px-4 py-2 text-sm font-medium transition-colors'
|
||||
>
|
||||
{_('Cancel')}
|
||||
</button>
|
||||
<button
|
||||
type='submit'
|
||||
disabled={busy}
|
||||
className={clsx(
|
||||
'btn btn-primary text-primary-content hover:bg-primary/90 active:bg-primary/80 rounded-lg border-0 px-4 py-2 text-sm font-medium transition-colors',
|
||||
busy && 'opacity-60',
|
||||
)}
|
||||
>
|
||||
{mode === 'set' ? _('Set PIN') : mode === 'change' ? _('Change PIN') : _('Disable')}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</dialog>
|
||||
</ModalPortal>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
import { derivePbkdf2Key, exportRawKey } from './derive';
|
||||
|
||||
/**
|
||||
* 4-digit numeric PIN that gates the app on launch when
|
||||
* `SystemSettings.pinCodeEnabled` is true. The threat model is
|
||||
* "casual physical access by another person on a shared device" —
|
||||
* peace of mind, not defense against an attacker who has filesystem
|
||||
* access (they can just delete settings.json). The PIN is hashed with
|
||||
* a per-user salt so it never sits as plaintext in settings.json.
|
||||
*/
|
||||
export const PIN_LENGTH = 4;
|
||||
export const PIN_SALT_BYTES = 16;
|
||||
|
||||
/**
|
||||
* PBKDF2 iteration count for PIN hashing. Lower than the sync-passphrase
|
||||
* count (600k) because the PIN is entered every app launch and the
|
||||
* attacker model is bounded — local filesystem access trivially
|
||||
* defeats any number of iterations. 100k keeps the unlock latency
|
||||
* snappy (~50–100ms on typical hardware) while still adding a
|
||||
* meaningful per-attempt cost.
|
||||
*/
|
||||
export const PIN_PBKDF2_ITERATIONS = 100_000;
|
||||
|
||||
const PIN_RE = /^[0-9]{4}$/;
|
||||
|
||||
const bytesToHex = (b: Uint8Array): string => {
|
||||
let s = '';
|
||||
for (let i = 0; i < b.length; i++) s += b[i]!.toString(16).padStart(2, '0');
|
||||
return s;
|
||||
};
|
||||
|
||||
const hexToBytes = (hex: string): Uint8Array => {
|
||||
if (hex.length % 2 !== 0) throw new Error('Invalid hex string');
|
||||
const out = new Uint8Array(hex.length / 2);
|
||||
for (let i = 0; i < out.length; i++) {
|
||||
out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
||||
}
|
||||
return out;
|
||||
};
|
||||
|
||||
const constantTimeStringEq = (a: string, b: string): boolean => {
|
||||
if (a.length !== b.length) return false;
|
||||
let diff = 0;
|
||||
for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
||||
return diff === 0;
|
||||
};
|
||||
|
||||
export const isValidPin = (pin: string): boolean => PIN_RE.test(pin);
|
||||
|
||||
export const generatePinSalt = (): string =>
|
||||
bytesToHex(crypto.getRandomValues(new Uint8Array(PIN_SALT_BYTES)));
|
||||
|
||||
export const hashPin = async (pin: string, saltHex: string): Promise<string> => {
|
||||
const key = await derivePbkdf2Key(pin, hexToBytes(saltHex), PIN_PBKDF2_ITERATIONS);
|
||||
return bytesToHex(await exportRawKey(key));
|
||||
};
|
||||
|
||||
export const verifyPin = async (
|
||||
pin: string,
|
||||
saltHex: string,
|
||||
storedHashHex: string,
|
||||
): Promise<boolean> => {
|
||||
const computed = await hashPin(pin, saltHex);
|
||||
return constantTimeStringEq(computed, storedHashHex);
|
||||
};
|
||||
@@ -111,6 +111,8 @@ export const DEFAULT_SYSTEM_SETTINGS: Partial<SystemSettings> = {
|
||||
metadataOthersCollapsed: false,
|
||||
metadataDescriptionCollapsed: false,
|
||||
|
||||
pinCodeEnabled: false,
|
||||
|
||||
customDictionaries: [],
|
||||
dictionarySettings: {
|
||||
providerOrder: ['builtin:wiktionary', 'builtin:wikipedia'],
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
import { create } from 'zustand';
|
||||
|
||||
export type AppLockDialogMode = 'set' | 'change' | 'disable';
|
||||
|
||||
interface AppLockState {
|
||||
/**
|
||||
* Has the gate been initialized from on-disk settings yet? Until
|
||||
* this flips to `true` the gate renders nothing — guarantees the
|
||||
* library/reader can never flash on screen before the lock screen
|
||||
* does.
|
||||
*/
|
||||
isInitialized: boolean;
|
||||
|
||||
/**
|
||||
* Session-scoped unlock flag. `true` if there is no PIN configured
|
||||
* OR the user has entered the correct PIN since this page load.
|
||||
* Flipping back to `false` is reserved for future re-lock-on-resume
|
||||
* work; today nothing calls `lock()` after the initial unlock.
|
||||
*/
|
||||
isUnlocked: boolean;
|
||||
|
||||
/** Cached copy of the PIN salt + hash — mirrors `SystemSettings`. */
|
||||
pinHash: string | null;
|
||||
pinSalt: string | null;
|
||||
|
||||
/** Called once from `Providers` after `loadSettings` resolves. */
|
||||
initialize: (config: { enabled: boolean; hash?: string; salt?: string }) => void;
|
||||
|
||||
/** Called by `<AppLockScreen />` after a verified PIN entry. */
|
||||
unlock: () => void;
|
||||
|
||||
/** Reserved for future re-lock work (background timeout, etc.). */
|
||||
lock: () => void;
|
||||
|
||||
/** Called from the settings dialog after persisting a new/changed PIN. */
|
||||
setPin: (hash: string, salt: string) => void;
|
||||
|
||||
/** Called from the settings dialog after disabling the lock. */
|
||||
clearPin: () => void;
|
||||
|
||||
/**
|
||||
* Which app-lock dialog (if any) is currently open. Lifted out of
|
||||
* `SettingsMenu` because that component unmounts when its dropdown
|
||||
* closes — local dialog state would never get to render.
|
||||
*/
|
||||
dialogMode: AppLockDialogMode | null;
|
||||
openDialog: (mode: AppLockDialogMode) => void;
|
||||
closeDialog: () => void;
|
||||
}
|
||||
|
||||
export const useAppLockStore = create<AppLockState>((set) => ({
|
||||
isInitialized: false,
|
||||
isUnlocked: true,
|
||||
pinHash: null,
|
||||
pinSalt: null,
|
||||
initialize: ({ enabled, hash, salt }) =>
|
||||
set({
|
||||
isInitialized: true,
|
||||
isUnlocked: !enabled,
|
||||
pinHash: hash ?? null,
|
||||
pinSalt: salt ?? null,
|
||||
}),
|
||||
unlock: () => set({ isUnlocked: true }),
|
||||
lock: () => set({ isUnlocked: false }),
|
||||
setPin: (hash, salt) => set({ pinHash: hash, pinSalt: salt }),
|
||||
clearPin: () => set({ pinHash: null, pinSalt: null, isUnlocked: true }),
|
||||
dialogMode: null,
|
||||
openDialog: (mode) => set({ dialogMode: mode }),
|
||||
closeDialog: () => set({ dialogMode: null }),
|
||||
}));
|
||||
@@ -822,6 +822,29 @@ body.atmosphere #atmosphere-overlay {
|
||||
animation: shake 0.8s ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes pin-shake {
|
||||
0%,
|
||||
100% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
20% {
|
||||
transform: translateX(-8px);
|
||||
}
|
||||
40% {
|
||||
transform: translateX(8px);
|
||||
}
|
||||
60% {
|
||||
transform: translateX(-6px);
|
||||
}
|
||||
80% {
|
||||
transform: translateX(6px);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-pin-shake {
|
||||
animation: pin-shake 0.4s ease-in-out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Suppress the daisyUI / browser focus ring on text inputs and
|
||||
* textareas. The element's own border (input-bordered, textarea-bordered)
|
||||
|
||||
@@ -132,6 +132,17 @@ export interface SystemSettings {
|
||||
metadataOthersCollapsed: boolean;
|
||||
metadataDescriptionCollapsed: boolean;
|
||||
|
||||
/**
|
||||
* App-lock PIN. When `pinCodeEnabled` is true, the user must enter
|
||||
* a 4-digit PIN before the library/reader is rendered on app launch.
|
||||
* `pinCodeHash` is `bytesToHex(PBKDF2-SHA256(pin, hexToBytes(pinCodeSalt)))`,
|
||||
* never the plaintext PIN. Cleared together with `pinCodeEnabled = false`
|
||||
* when the user disables the lock.
|
||||
*/
|
||||
pinCodeEnabled?: boolean;
|
||||
pinCodeHash?: string;
|
||||
pinCodeSalt?: string;
|
||||
|
||||
kosync: KOSyncSettings;
|
||||
readwise: ReadwiseSettings;
|
||||
hardcover: HardcoverSettings;
|
||||
|
||||
Reference in New Issue
Block a user