diff --git a/.eslintignore b/.eslintignore index 1c0b05c32..4df47d5fd 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,5 +1,5 @@ node_modules -dist +js firefox chrome scripts diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index fb23f2562..000000000 --- a/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 46385937e..9d9e899cd 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -36,3 +36,22 @@ body: description: The extension version is found by clicking the cog at the top left and looking at the bottom of the page. validations: required: true + - type: dropdown + attributes: + label: Affected area + description: Optional — helps us triage which part of the extension is involved. + options: + - Not sure / general + - Generating codes + - Cloud backup (Dropbox) + - Autofill on websites + - Import / export + - Security advisor + validations: + required: false + - type: textarea + attributes: + label: Console errors + description: Optional. Open the extension page, press F12 → Console, and paste any red errors. Remove any OTP codes or secrets before posting. + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 5e5da54af..167557dac 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,7 +1,4 @@ contact_links: - name: Feature Request - url: https://github.com/Authenticator-Extension/Authenticator/discussions/new + url: https://github.com/Hank076/Authenticator/discussions/new about: Please request features here. - - name: Translation Issues - url: https://crowdin.com/project/authenticator-firefox - about: Please correct translation issues on Crowdin. diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 5cdff73ad..e2c86d38b 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -13,42 +13,18 @@ jobs: analyse: name: Analyse runs-on: ubuntu-latest + permissions: + contents: read + security-events: write steps: - name: Checkout repository - uses: actions/checkout@v2 - with: - # We must fetch at least the immediate parents so that if this is - # a pull request then we can checkout the head. - fetch-depth: 2 - - # If this run was triggered by a pull request event, then checkout - # the head of the pull request instead of the merge commit. - - run: git checkout HEAD^2 - if: ${{ github.event_name == 'pull_request' }} + uses: actions/checkout@v5 - # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v1 - # Override language selection by uncommenting this and choosing your languages - # with: - # languages: go, javascript, csharp, python, cpp, java - - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v1 - - # ℹ️ Command-line programs to run using the OS shell. - # 📚 https://git.io/JvXDl - - # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines - # and modify them (or add more) to build your code if your project - # uses a compiled language - - #- run: | - # make bootstrap - # make release + uses: github/codeql-action/init@v3 + with: + languages: javascript-typescript - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/i18n.yml b/.github/workflows/i18n.yml index a9e870850..36a6ef5b3 100644 --- a/.github/workflows/i18n.yml +++ b/.github/workflows/i18n.yml @@ -1,17 +1,11 @@ name: i18n -on: - push: - branches: [ dev ] +on: [push, pull_request] jobs: - i18n-strings: + key-parity: runs-on: ubuntu-latest - name: Process new i18n strings - + name: Check locale key parity steps: - - uses: actions/checkout@v2 - - - run: bash scripts/i18n.sh - env: - DEPLOY_KEY_PASSWORD: ${{ secrets.DEPLOY_KEY_PASSWORD }} \ No newline at end of file + - uses: actions/checkout@v5 + - run: node scripts/check-i18n.js diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c4e2793f3..dbd71172f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -8,10 +8,12 @@ jobs: name: Style checks steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v5 - name: Setup Node.js environment - uses: actions/setup-node@v2.1.2 + uses: actions/setup-node@v5 + with: + node-version: 22 - name: Install dependencies run: | @@ -29,10 +31,13 @@ jobs: platform: ["chrome", "firefox"] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v5 - name: Setup Node.js environment - uses: actions/setup-node@v2.1.2 + uses: actions/setup-node@v5 + with: + node-version: 22 + cache: npm - name: Install dependencies run: npm ci @@ -45,17 +50,16 @@ jobs: needs: [build] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v5 - name: Setup Node.js environment - uses: actions/setup-node@v2.1.2 + uses: actions/setup-node@v5 + with: + node-version: 22 + cache: npm - name: Install dependencies run: npm ci - env: - PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: "true" - name: Test code - uses: mymindstorm/puppeteer-headful@8f745c770f7f4c0f9f332d7c43a775f90e53779a - with: - args: npm test + run: xvfb-run --auto-servernum -- npm test diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 189520d13..503eb3a64 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -2,45 +2,40 @@ name: Release on: push: - tags: - - 'v*' + tags: + - "v*" + +permissions: + contents: write jobs: release: runs-on: ubuntu-latest - name: Publish release build - + name: Build and publish Chrome extension ZIP + steps: - - uses: actions/checkout@v2 - + - uses: actions/checkout@v5 + - name: Setup Node.js environment - uses: actions/setup-node@v2.1.2 - + uses: actions/setup-node@v5 + with: + node-version: 22 + cache: npm + - name: Install dependencies run: npm ci - - - name: Build - run: bash scripts/release.sh - env: - CREDS_FILE_PASSWORD: ${{ secrets.CREDS_FILE_PASSWORD }} - - - name: Create a release - id: create_release - uses: actions/create-release@v1.1.4 env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag_name: ${{ github.ref }} - release_name: Release ${{ github.ref }} - draft: false - prerelease: false - - name: Upload Release Asset - id: upload-release-asset - uses: actions/upload-release-asset@v1 + PUPPETEER_SKIP_DOWNLOAD: "true" + + - name: Build + run: npm run chrome + + - name: Package + run: cd chrome && zip -r "../OTPilot-Authenticator-chrome-${GITHUB_REF_NAME}.zip" . + + - name: Create release + run: | + git fetch --force origin "refs/tags/${GITHUB_REF_NAME}:refs/tags/${GITHUB_REF_NAME}" + gh release create "$GITHUB_REF_NAME" "OTPilot-Authenticator-chrome-${GITHUB_REF_NAME}.zip" --title "$GITHUB_REF_NAME" --notes-from-tag env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./release.tar.gz - asset_name: release.tar.gz - asset_content_type: application/gzip + GH_TOKEN: ${{ github.token }} diff --git a/.github/workflows/tagging.yml b/.github/workflows/tagging.yml deleted file mode 100644 index 8a034354f..000000000 --- a/.github/workflows/tagging.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: Tagging - -on: - push: - branches: [ release ] - -jobs: - tagging: - runs-on: ubuntu-latest - name: Release tagging - - steps: - - uses: actions/checkout@v2 - - - run: bash scripts/tag.sh - env: - DEPLOY_KEY_PASSWORD: ${{ secrets.DEPLOY_KEY_PASSWORD }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index d2335a07e..31d235d1c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,16 +1,34 @@ +# dependencies node_modules + +# build output build chrome* firefox* edge* -dist -.vscode -.atom-build.yml -scripts/authenticator-build-key.enc -cert.pfx +js css +/test .license-gen-tmp view/licenses.html -./test +scripts/test-runner.js + +# release credentials scripts/authenticator-build-key -scripts/test-runner.js \ No newline at end of file +scripts/authenticator-build-key.enc +cert.pfx + +# local docs / AI working notes +docs/ + +# editor & local tool settings +.vscode +.claude/settings.local.json + +# OS +Thumbs.db +Desktop.ini +.DS_Store + +# npm logs +npm-debug.log* diff --git a/README.md b/README.md index bff8ebe38..6ae53eec9 100644 --- a/README.md +++ b/README.md @@ -1,54 +1,56 @@ -# Authenticator [![Build Status](https://travis-ci.com/Authenticator-Extension/Authenticator.svg?branch=dev)](https://travis-ci.com/Authenticator-Extension/Authenticator) [![Crowdin](https://d322cqt584bo4o.cloudfront.net/authenticator-firefox/localized.svg)](https://crowdin.com/project/authenticator-firefox) +# OTPilot Authenticator -> Authenticator generates 2-Step Verification codes in your browser. +> OTPilot Authenticator generates 2-Step Verification (OTP) codes in your browser. -## Available for Chrome, Firefox, and Microsoft Edge +[**Install from the Chrome Web Store**](https://chromewebstore.google.com/detail/otpilot-authenticator/compmlfocdmifebgiecbajaidkmgkonc) -[](https://chrome.google.com/webstore/detail/authenticator/bhghoamapcdpbohphigoooaddinpkbai) [](https://addons.mozilla.org/en-US/firefox/addon/auth-helper?src=external-github) [](https://microsoftedge.microsoft.com/addons/detail/ocglkepbibnalbgmbachknglpdipeoio) +This is a fork of [Authenticator-Extension/Authenticator](https://github.com/Authenticator-Extension/Authenticator) ([source of this fork](https://github.com/Hank076/Authenticator)). On top of upstream, this fork adds: - -### Safari Edition - -A Safari edition of Authenticator is available on the App Store. We do not provide official support for the Safari edition. - -[Download on the App Store](https://apps.apple.com/us/app/authen/id1602945200?mt=12) +- **Encrypted cloud backup to Dropbox** — OAuth Authorization Code + PKCE via `chrome.identity.launchWebAuthFlow`; no client secret ships in the extension. (Google Drive backup was removed in 2026-07; OneDrive is a disabled placeholder.) +- **Advisor** — security recommendations for your accounts. +- **Host-bound autofill** — codes are only injected when the page host matches the account. +- **Redesigned import flow** and a CSS custom-property design system (`sass/_tokens.scss`). ## Build Setup -``` bash -# install development dependencies -npm install -# compile -npm run [chrome, firefox, prod] -``` - -To reproduce a build: - -``` bash +```bash +# install dependencies (~2 min, downloads puppeteer Chromium) npm ci -npm run prod + +# build for a target +npm run [chrome, firefox, edge, prod] ``` -To reproduce a build for Safari, please follow contribution guidance in [Authenticator-Extension/Authen](https://github.com/Authenticator-Extension/Authen#how-to-contribute) +Notes: + +- Build output goes to `chrome/` (bundles under `chrome/js/`); these directories are gitignored. +- `npm run prod` aborts if `src/models/credentials.ts` is empty (OAuth credentials are kept out of the repo). `chrome`/`test` builds only warn — everything works except live cloud backup. +- **Windows**: builds run through `bash scripts/build.sh`, so use a full [Git Bash](https://git-scm.com/download/win) (with complete coreutils). Running `npm test` from plain PowerShell fails with `'bash' is not recognized`. ## Development (Chrome) -``` bash -# install development dependencies -npm install -# compiles the Chrome extension to the `./test/chrome` directory +```bash +# watch build npm run dev:chrome -# load the unpacked extension from the `./test/chrome/ directory in Chrome +# then load the unpacked extension from the `chrome/` directory ``` -Note that Windows users should download a tool like [Git Bash](https://git-scm.com/download/win) or [Cygwin](http://cygwin.com/) to build. +## Testing -## Acknowledgment +```bash +npm test +``` + +Runs the extension in a real browser via puppeteer with in-browser mocha (`headless: false`, so a GUI is required). -We would like to extend our heartfelt thanks to Laurent, the Chief Information Security Officer (CISO) of the University of Luxembourg, for the invaluable support and contribution to this project. During the development process, the CISO team provided critical security recommendations that helped us identify and address potential vulnerabilities, significantly enhancing the security and reliability of the project. +## i18n -We especially want to acknowledge the University of Luxembourg's information security team for their selfless contribution, which not only facilitated the progress of this project but also had a positive impact on the broader open-source community. We recognize that the success of open-source software depends heavily on collaboration and support from various stakeholders, and the involvement of the University of Luxembourg has allowed us to offer a more secure and dependable product to a wider audience. +`_locales/en/messages.json` is the source of truth for keys. Crowdin is no longer used; other locales are maintained by hand. All locales must have the exact same key set as `en`, enforced by: -We understand that while open-source software is free, maintaining and improving these projects requires significant resources. The University of Luxembourg’s information security team has demonstrated their strong commitment to the open-source community, contributing not just within their university but to users and developers globally. We hope this acknowledgment will help them continue to secure the support and resources necessary to further advance open-source initiatives. +```bash +node scripts/check-i18n.js +``` + +## Acknowledgment -Once again, we express our sincere gratitude to the University of Luxembourg's CISO team for their valuable advice and assistance. +We would like to extend our heartfelt thanks to Laurent, the Chief Information Security Officer (CISO) of the University of Luxembourg, and the university's information security team for their invaluable security recommendations to the upstream project. diff --git a/_locales/ar/messages.json b/_locales/ar/messages.json index 9afd6c792..7229b1d6c 100644 --- a/_locales/ar/messages.json +++ b/_locales/ar/messages.json @@ -1,14 +1,22 @@ { "extName": { - "message": "الموثق", + "message": "OTPilot Authenticator", "description": "Extension Name." }, "extShortName": { - "message": "المدقق", + "message": "OTPilot", "description": "Extension Short Name." }, + "host": { + "message": "الموقع الإلكتروني", + "description": "Bound website host used to restrict autofill to a matching page." + }, + "backup_requires_password": { + "message": "قم بتعيين كلمة مرور أولاً لاستخدام النسخ الاحتياطي السحابي، حتى يتم تشفير أسرارك قبل مغادرتها هذا الجهاز.", + "description": "Shown in the cloud backup section when no master password is set." + }, "extDesc": { - "message": "يقوم المصادق بإنشاء رموز المصادقة ذات عاملين في المتصفح الخاص بك.", + "message": "يُنشئ OTPilot Authenticator رموز المصادقة الثنائية (2FA)، مع نسخ احتياطي سحابي مشفّر، وتعبئة تلقائية مرتبطة بالموقع، ونصائح أمنية.", "description": "Extension Description." }, "added": { @@ -16,13 +24,17 @@ "description": "Added Account." }, "errorqr": { - "message": "رمز استجابة سريع غير معروف.", + "message": "رمز QR غير معروف.", "description": "QR Error." }, "errorsecret": { - "message": "الإجابة السرية الخاص غير مطابق", + "message": "المفتاح السري للحساب غير صالح", "description": "Secret Error." }, + "errorissuer": { + "message": "لا يمكن أن يحتوي المصدر على \"::\"", + "description": "Issuer Error." + }, "add_code": { "message": "إضافة حساب", "description": "Add account." @@ -40,19 +52,15 @@ "description": "Import migration data failed." }, "migration_partly_fail": { - "message": "بعض من بيانات الحساب لم يتم استيرادها بنجاح.", + "message": "بعض بيانات الحساب لم يتم استيرادها بنجاح.", "description": "Some migration data is broken." }, - "close": { - "message": "إغلاق", - "description": "Close." - }, "ok": { "message": "موافق", "description": "OK." }, "yes": { - "message": "إجابه", + "message": "نعم", "description": "Yes." }, "no": { @@ -72,7 +80,7 @@ "description": "Issuer." }, "secret": { - "message": "سري", + "message": "المفتاح السري", "description": "Secret." }, "updateSuccess": { @@ -84,25 +92,21 @@ "description": "Update Failure." }, "about": { - "message": "نبذه عن", + "message": "حول", "description": "About." }, "settings": { - "message": "الاعدادات", + "message": "الإعدادات", "description": "Settings." }, "security": { - "message": "حماية الخصوصية", + "message": "الأمان", "description": "Security." }, "current_phrase": { "message": "كلمة المرور الحالية", "description": "Current Passphrase." }, - "new_phrase": { - "message": "كلمة السرّ الجديدة", - "description": "New Passphrase." - }, "phrase": { "message": "كلمة المرور", "description": "Passphrase." @@ -120,29 +124,25 @@ "description": "Message that user is required to acknowledge before clearing all data." }, "delete_all": { - "message": "إعادة تعيين Authenticator" + "message": "إعادة تعيين OTPilot" }, "delete_all_warning": { - "message": "سيؤدي هذا إلى حذف جميع بياناتك وإعادة تعيين Authenticator بالكامل. لن تتمكن من استعادة أي بيانات محذوفة! يجب مراعاة حفظ نسخة احتياطية قبل إعادة تعيين Authenticator." + "message": "سيؤدي هذا إلى حذف جميع بياناتك وإعادة تعيين OTPilot بالكامل. لن تتمكن من استعادة أي بيانات محذوفة! يجب مراعاة حفظ نسخة احتياطية قبل إعادة تعيين OTPilot." }, "security_warning": { - "message": "هذا الكود الخاص سوف يستخدم لتشفير حسابك.\nلا نقدر على مساعدتك إذا فقدت هذا الكود الخاص.", + "message": "ستُستخدم كلمة المرور هذه لتشفير حساباتك. لن يتمكن أحد من مساعدتك إذا نسيت كلمة المرور.", "description": "Passphrase Warning." }, - "update": { - "message": "تطوير", - "description": "Update." - }, "phrase_incorrect": { - "message": "لن تقدر على فتح حساب جديد حتى يتم فك تشفير كل الحسابات. الرجاء إضافة كلمة المرور الصحيحة لاستكمال العملية.", + "message": "لا يمكنك إضافة حساب جديد حتى يتم فك تشفير جميع الحسابات. يرجى إدخال كلمة المرور الصحيحة قبل المتابعة.", "description": "Passphrase Incorrect." }, "phrase_incorrect_export": { - "message": "الحسابات الغير قابله لفك التشفير لان يتم تضمينها إلى النسخة الاحتياطية.", + "message": "الحسابات التي تعذّر فك تشفيرها لن يتم تضمينها في هذه النسخة الاحتياطية.", "description": "Skip Unable-decripted Data." }, "phrase_not_match": { - "message": "كلمة السر غير صحيحة.", + "message": "كلمتا المرور غير متطابقتين.", "description": "Passphrase Not Match." }, "encrypted": { @@ -150,19 +150,15 @@ "description": "Encrypted." }, "copied": { - "message": "تَمَّ النَّسخ", + "message": "تم النسخ", "description": "Copied." }, "feedback": { - "message": "التقييم والملاحظات", + "message": "الملاحظات", "description": "Feedback." }, - "translate": { - "message": "ترجمة", - "description": "Translate." - }, "source": { - "message": "المصدر", + "message": "الكود المصدري", "description": "Source Code." }, "passphrase_info": { @@ -173,24 +169,20 @@ "message": "مزامنة الساعة مع جوجل", "description": "Sync Clock" }, - "remember_phrase": { - "message": "تذكر كلمة السر", - "description": "Remember Passphrase" - }, "clock_too_far_off": { "message": "تحذير! التوقيت المحلي بعيد جدًا، الرجاء إصلاحه قبل المتابعة.", "description": "Local Time is Too Far Off" }, "remind_backup": { - "message": "هل لديك نسخة احتياطية للحسابات الخاصة بك؟ لا تنتظر حتى تصبح متأخرًا", + "message": "هل لديك نسخة احتياطية للحسابات الخاصة بك؟ لا تنتظر حتى يفوت الأوان!", "description": "Remind Backup" }, "capture_failed": { - "message": "فشل في التقاط الرمز, يرجى إعادة تحميل الصفحة والمحاولة مرة أخرى.", + "message": "فشل التقاط الرمز، يرجى إعادة تحميل الصفحة والمحاولة مرة أخرى.", "description": "Capture Failed" }, "capture_local_file_failed": { - "message": "Are you trying to scan QR code from a local file? Use Import QR Image Backup instead.", + "message": "هل تحاول مسح رمز QR من ملف محلي؟ استخدم خيار «استيراد نسخة احتياطية لصورة QR» بدلاً من ذلك.", "description": "Import QR image backup instead of scan local image" }, "based_on_time": { @@ -198,7 +190,7 @@ "description": "Time Based" }, "based_on_counter": { - "message": "Counter Based", + "message": "على أساس العداد", "description": "Counter Based" }, "resize_popup_page": { @@ -230,17 +222,49 @@ "description": "Import qr image backup." }, "import_qr_images": { - "message": "قم بادخال صورة الكود", + "message": "استيراد صور QR", "description": "Import qr images. Shown as add account method." }, "import_backup_code": { - "message": "استيراد نسخة احتياطية نصية", + "message": "استيراد عناوين OTP", "description": "Import backup code." }, "import_otp_urls": { "message": "استيراد عناوين OTP", "description": "Import OTP URLs. Shown as add account method." }, + "import_drop_file": { + "message": "أفلت ملف النسخة الاحتياطية هنا أو انقر للتصفح", + "description": "File import dropzone title" + }, + "import_file_accepts": { + "message": "يقبل ملفات التصدير بصيغة ‎.json و‎.txt", + "description": "File import dropzone hint" + }, + "import_choose_file": { + "message": "اختر ملفًا", + "description": "File import dropzone button" + }, + "import_encrypted_note": { + "message": "إذا كان الملف مشفرًا، سيُطلب منك إدخال كلمة المرور الخاصة به بعد تحديده.", + "description": "File import note about encrypted backups" + }, + "import_enc_required": { + "message": "مشفَّر — يتطلب كلمة مرور", + "description": "Badge on a picked encrypted backup file" + }, + "import_pass_placeholder": { + "message": "أدخل كلمة مرور الملف", + "description": "Passphrase input placeholder for encrypted import" + }, + "import_pass_hint": { + "message": "هذه هي كلمة المرور التي قمت بتعيينها عند إنشاء النسخة الاحتياطية.", + "description": "Hint under the import passphrase field" + }, + "import_decrypt": { + "message": "فك التشفير والاستيراد", + "description": "Button to decrypt and import an encrypted backup" + }, "import_backup_qr_partly_failed": { "message": "تم الاستيراد بنجاح، ولكن لم يتم التعرف على بعض رموز QR.", "description": "Import successful, but some QR image cannot be recognized." @@ -297,25 +321,117 @@ "message": "استخدام الملء التلقائي", "description": "Use Autofill" }, - "use_high_contrast": { - "message": "استخدام التباين العالي", - "description": "Use High Contrast" - }, "theme": { "message": "السمة", "description": "Theme" }, + "settings_appearance": { + "message": "المظهر", + "description": "Settings section: appearance" + }, + "settings_general": { + "message": "عام", + "description": "Settings section: general" + }, + "edit_accounts": { + "message": "تحرير الحسابات", + "description": "Header title while in edit mode" + }, + "show_qr": { + "message": "إظهار رمز QR", + "description": "Context-menu action to show an account's QR code" + }, + "pin_to_top": { + "message": "تثبيت في الأعلى", + "description": "Context-menu action to pin an account to the top" + }, + "unpin": { + "message": "إلغاء التثبيت", + "description": "Context-menu action to unpin an account" + }, + "vault_locked": { + "message": "الخزنة مقفلة", + "description": "Title on the unlock screen" + }, + "unlock": { + "message": "فتح القفل", + "description": "Unlock button" + }, + "qr_transfer_title": { + "message": "نقل هذا الحساب", + "description": "QR sheet caption title" + }, + "qr_transfer_desc": { + "message": "امسح هذا الرمز باستخدام OTPilot على جهاز آخر لنقل هذا الحساب — فهو لا يغادر أجهزتك أبدًا.", + "description": "QR sheet caption body" + }, + "backup_on_device": { + "message": "على هذا الجهاز", + "description": "Backup section: local" + }, + "backup_cloud_sync": { + "message": "المزامنة السحابية", + "description": "Backup section: cloud" + }, + "backup_connected": { + "message": "متصل", + "description": "Cloud provider connected status" + }, + "backup_not_connected": { + "message": "غير متصل", + "description": "Cloud provider not-connected status" + }, + "showing_codes_for": { + "message": "عرض الرموز الخاصة بـ", + "description": "Smart-filter banner prefix, followed by the site domain" + }, + "other_accounts": { + "message": "حسابات أخرى", + "description": "Divider above non-matching accounts in the filter view" + }, + "filter_to_site": { + "message": "تصفية إلى", + "description": "Banner prefix to re-apply the site filter, followed by the domain" + }, + "onboarding_title": { + "message": "رموز تصاحبك أينما ذهبت", + "description": "Onboarding headline" + }, + "onboarding_subtitle": { + "message": "أنشئ رموز مصادقة ثنائية آمنة لكل حساب — مشفّرة، تعمل دون اتصال، وعلى بُعد نقرة واحدة من النسخ دائمًا.", + "description": "Onboarding subtitle" + }, + "onboarding_feature_offline": { + "message": "يعمل بالكامل دون اتصال بالإنترنت — لا شيء يغادر جهازك", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_encrypted": { + "message": "خزنة مشفّرة مع قفل اختياري بكلمة مرور", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_scan": { + "message": "امسح رمز QR أو استورد نسخة احتياطية موجودة", + "description": "Onboarding feature bullet" + }, + "onboarding_get_started": { + "message": "ابدأ الآن", + "description": "Onboarding primary button" + }, + "onboarding_import": { + "message": "لدي نسخة احتياطية لاستيرادها", + "description": "Onboarding secondary button" + }, "theme_light": { - "message": "مضيء", + "message": "فاتح", "description": "Light theme" }, "theme_dark": { - "message": "مظلم", + "message": "داكن", "description": "Dark theme" }, - "theme_simple": { - "message": "بسيط", - "description": "Simple theme" + "theme_auto": { + "message": "تلقائي (حسب النظام)", + "description": "Follow the system light/dark setting" }, "theme_compact": { "message": "مدمج", @@ -325,14 +441,6 @@ "message": "تباين عالي", "description": "High Contrast theme" }, - "theme_flat": { - "message": "مسطحة", - "description": "Flat theme" - }, - "storage_sync_info": { - "message": "النسخ الاحتياطي التلقائي لبياناتك إلى خِدْمَات تخزين طرف ثالث.", - "description": "3rd party backup info" - }, "browser_sync": { "message": "مزامنة المتصفح", "description": "Storage location" @@ -362,10 +470,6 @@ } } }, - "otp_unsupported_warn": { - "message": "لديك حساب أو أكثر من Steam أو Blizzard لن تستخدم النسخ الاحتياطية غير المشفرة تنسيق النسخ الاحتياطي الموحد.", - "description": "Warning if using account that is not supported by standard backup format." - }, "otp_backup_inform": { "message": "يمكنك استيراد نسخ احتياطية من بعض التطبيقات الأخرى.", "description": "Info text on import page" @@ -378,7 +482,7 @@ "message": "جاري التحميل..." }, "autolock": { - "message": "القفْل بعد" + "message": "القفل بعد" }, "minutes": { "message": "دقائق" @@ -393,13 +497,13 @@ "message": "نوع" }, "invalid": { - "message": "معطل" + "message": "غير صالح" }, "digits": { - "message": "وحدات" + "message": "أرقام" }, "algorithm": { - "message": "‮الخوارزمية" + "message": "الخوارزمية" }, "smart_filter": { "message": "الفلتر الذكي" @@ -407,9 +511,6 @@ "backup": { "message": "نسخ احتياطي" }, - "backup_file_info": { - "message": "قم بالنسخ الاحتياطي لبياناتك إلى ملف." - }, "password_policy_default_hint": { "message": "كلمة المرور الخاصة بك لا تفي بمتطلبات أمان مؤسستك. اتصل بالمسؤول للحصول على مزيد من المعلومات." }, @@ -423,7 +524,7 @@ "message": "تفعيل القفل التلقائي لحماية بياناتك." }, "advisor_insight_browser_sync_not_enabled": { - "message": "مزامنة المتصفح معطلة. تمكينه يسمح للمزامنة بين الحسابات عبر المتصفحات." + "message": "مزامنة المتصفح معطّلة. تفعيلها يتيح مزامنة الحسابات بين المتصفحات." }, "advisor_insight_auto_fill_not_enabled": { "message": "يمكن تمكين التعبئة التلقائية لملء الرموز تلقائياً في مواقع الويب." @@ -435,7 +536,7 @@ "message": "إظهار جميع الرؤى." }, "no_insight_available": { - "message": "لم يتم العثور على رؤية، كل شيء يبدو جيدًا!" + "message": "لم يتم العثور على أي رؤى، كل شيء يبدو جيدًا!" }, "danger": { "message": "خطر" @@ -462,13 +563,13 @@ "message": "الصلاحيات" }, "permission_revoke": { - "message": "تعطيل" + "message": "إلغاء" }, "permission_show_required_permissions": { - "message": "إظهار الأذونات غير القابلة للتعطيل" + "message": "إظهار الصلاحيات غير القابلة للإلغاء" }, "permission_required": { - "message": "هذه الصلاحية اساسية و لا يمكن تعطيلها." + "message": "هذه الصلاحية أساسية ولا يمكن إلغاؤها." }, "permission_active_tab": { "message": "الوصول إلى علامة التبويب الحالية لمسح رموز QR." @@ -483,13 +584,13 @@ "message": "يسمح للقفل التلقائي بالعمل." }, "permission_scripting": { - "message": "أدخل النص في الشريط الظاهر لمسح رمز الاستجابة السريع والسماح للملء التلقائي بالعمل." + "message": "يُدرج نصوصًا برمجية في علامة التبويب الحالية لمسح رموز QR والسماح بعمل الملء التلقائي." }, "permission_clipboard_write": { "message": "يمنح صلاحية الكتابة فقط إلى الحافظة لنسخ الرموز إلى الحافظة عند النقر على الحساب." }, "permission_context_menus": { - "message": "يضيف Authenticator إلى القائمة." + "message": "يضيف OTPilot إلى القائمة." }, "permission_sync_clock": { "message": "يسمح بمزامنة الساعة مع جوجل." @@ -500,25 +601,17 @@ "permission_dropbox_cannot_revoke": { "message": "يجب عليك تعطيل النسخ الاحتياطي لـ Dropbox أولاً." }, - "permission_drive": { - "message": "يسمح بالنسخ الاحتياطي لGoogle Drive." - }, - "permission_drive_cannot_revoke": { - "message": "يجب عليك تعطيل النسخ الاحتياطي لـ Google Drive أولاً." - }, - "permission_onedrive": { - "message": "يسمح بالنسخ الاحتياطي لـ OneDrive." - }, - "permission_onedrive_cannot_revoke": { - "message": "يجب عليك تعطيل النسخ الاحتياطي لـ OneDrive أولاً." - }, "permission_unknown_permission": { "message": "صلاحية غير معروفة. إذا وجدت هذه الرسالة، يرجى إرسال تقرير عن الأخطاء." }, "phrase_wrong": { - "message": "Password incorrect" + "message": "كلمة المرور غير صحيحة" }, "activate_auto_filter": { - "message": "تحذير: يقوم المدقق الذكي بمطابقة اسم الاختصاص مع الحساب بشكل غير جيد. تأكد دائمًا من أنك على موقع التبويب الصحيح قبل إدخال الرمز!" + "message": "تحذير: يقوم الفلتر الذكي بمطابقة اسم النطاق مع الحساب بشكل تقريبي. تأكد دائمًا من أنك على الموقع الإلكتروني الصحيح قبل إدخال الرمز!" + }, + "backup_unavailable": { + "message": "غير متوفر بعد", + "description": "Shown on a cloud provider that is not implemented yet." } } diff --git a/_locales/bg/messages.json b/_locales/bg/messages.json index 74bd8712b..48f05c353 100644 --- a/_locales/bg/messages.json +++ b/_locales/bg/messages.json @@ -1,14 +1,22 @@ { "extName": { - "message": "Удостоверител", + "message": "OTPilot Authenticator", "description": "Extension Name." }, "extShortName": { - "message": "Удостоверяване", + "message": "OTPilot", "description": "Extension Short Name." }, + "host": { + "message": "Уебсайт", + "description": "Bound website host used to restrict autofill to a matching page." + }, + "backup_requires_password": { + "message": "Първо задайте парола, за да използвате облачно архивиране — така секретите ви се криптират, преди да напуснат това устройство.", + "description": "Shown in the cloud backup section when no master password is set." + }, "extDesc": { - "message": "Удостоверителя генерира 2-фактурна защита, за вашият браузър.", + "message": "OTPilot Authenticator генерира кодове за двуфакторна автентикация (2FA) с криптирано облачно архивиране, автоматично попълване, обвързано с уебсайта, и съвети за сигурност.", "description": "Extension Description." }, "added": { @@ -20,15 +28,19 @@ "description": "QR Error." }, "errorsecret": { - "message": "Невалиден клон на сметка", + "message": "Невалиден таен ключ на акаунта", "description": "Secret Error." }, + "errorissuer": { + "message": "Издателят не може да съдържа „::“", + "description": "Issuer Error." + }, "add_code": { "message": "Добави акаунт", "description": "Add account." }, "add_qr": { - "message": "Сканирайте QR кода", + "message": "Сканирай QR код", "description": "Scan QR Code." }, "add_secret": { @@ -36,17 +48,13 @@ "description": "Manual Entry." }, "migration_fail": { - "message": "Import failed. If you are migrating data from Google Authenticator, please re-export your data from Google Authenticator and try again.", + "message": "Импортирането е неуспешно. Ако мигрирате данни от Google Authenticator, моля, експортирайте отново данните си от Google Authenticator и опитайте пак.", "description": "Import migration data failed." }, "migration_partly_fail": { "message": "Някои данни за акаунта не бяха импортирани успешно.", "description": "Some migration data is broken." }, - "close": { - "message": "Затвори", - "description": "Close." - }, "ok": { "message": "Добре", "description": "OK." @@ -60,11 +68,11 @@ "description": "No." }, "account": { - "message": "Профил", + "message": "Акаунт", "description": "Account." }, "accountName": { - "message": "Username", + "message": "Потребителско име", "description": "Account Name." }, "issuer": { @@ -72,7 +80,7 @@ "description": "Issuer." }, "secret": { - "message": "Секретен", + "message": "Таен ключ", "description": "Secret." }, "updateSuccess": { @@ -84,7 +92,7 @@ "description": "Update Failure." }, "about": { - "message": "За Нас", + "message": "Относно", "description": "About." }, "settings": { @@ -96,19 +104,15 @@ "description": "Security." }, "current_phrase": { - "message": "Сегашна парола", + "message": "Текуща парола", "description": "Current Passphrase." }, - "new_phrase": { - "message": "Нова парола", - "description": "New Passphrase." - }, "phrase": { "message": "Парола", "description": "Passphrase." }, "confirm_phrase": { - "message": "Потвърди паролата", + "message": "Потвърдете паролата", "description": "Confirm Passphrase." }, "confirm_delete": { @@ -120,21 +124,17 @@ "description": "Message that user is required to acknowledge before clearing all data." }, "delete_all": { - "message": "Нулирай Удостоверител" + "message": "Нулирай OTPilot" }, "delete_all_warning": { - "message": "Това ще изтрие всичките ви данни и изцяло ще изтрие Удостоверителя. Няма да можете да възстановите изтритите данни! Препоръчваме за архивирате, преди да нулирате Удостоверителя." + "message": "Това ще изтрие всичките ви данни и напълно ще нулира OTPilot. Няма да можете да възстановите изтритите данни! Препоръчваме да архивирате данните си, преди да нулирате OTPilot." }, "security_warning": { - "message": "Тази парола ще се използва за кодиране на вашите акаунти. Никой не може да ви помогне, ако си забравите паролата.", + "message": "Тази парола ще се използва за криптиране на вашите акаунти. Никой не може да ви помогне, ако забравите паролата.", "description": "Passphrase Warning." }, - "update": { - "message": "Актуализация", - "description": "Update." - }, "phrase_incorrect": { - "message": "Не можете да добавите нов акаунт, докато акаунтът не се декриптира. Въведете правилната парола.", + "message": "Не можете да добавите нов акаунт, докато всички акаунти не бъдат декриптирани. Моля, въведете правилната парола, преди да продължите.", "description": "Passphrase Incorrect." }, "phrase_incorrect_export": { @@ -157,10 +157,6 @@ "message": "Обратна връзка", "description": "Feedback." }, - "translate": { - "message": "Превод", - "description": "Translate." - }, "source": { "message": "Изходен код", "description": "Source Code." @@ -173,12 +169,8 @@ "message": "Синхронизиране на часовника с Google", "description": "Sync Clock" }, - "remember_phrase": { - "message": "Запомни паролата", - "description": "Remember Passphrase" - }, "clock_too_far_off": { - "message": "Внимание! Вашият часовник не е верен, моля го оправя преди да продължите.", + "message": "Внимание! Локалният ви часовник е твърде неточен. Моля, коригирайте го, преди да продължите.", "description": "Local Time is Too Far Off" }, "remind_backup": { @@ -186,19 +178,19 @@ "description": "Remind Backup" }, "capture_failed": { - "message": "Заснемането неуспешно, моля презаредете страницата и опитайте отново.", + "message": "Заснемането е неуспешно, моля презаредете страницата и опитайте отново.", "description": "Capture Failed" }, "capture_local_file_failed": { - "message": "Опитвате се да сканирате QR код от локален файл? Use Вместо това импортирайте резервно копие на QR изображението.", + "message": "Опитвате се да сканирате QR код от локален файл? Вместо това използвайте „Импорт на резервно копие на QR изображение“.", "description": "Import QR image backup instead of scan local image" }, "based_on_time": { - "message": "Време на базата", + "message": "Базирано на време", "description": "Time Based" }, "based_on_counter": { - "message": "На база брояч", + "message": "Базирано на брояч", "description": "Counter Based" }, "resize_popup_page": { @@ -210,7 +202,7 @@ "description": "Scale" }, "export_info": { - "message": "Предупреждение: всички резервни копия са декриптирани. Искате ли да добавите акаунт към друго приложение? Задръжте над горната част на всяка сметка и натиснете скрития бутон.", + "message": "Предупреждение: всички резервни копия са некриптирани. Искате ли да добавите акаунт към друго приложение? Задръжте показалеца над горния десен ъгъл на акаунта и натиснете скрития бутон.", "description": "Export menu info text" }, "download_backup": { @@ -234,19 +226,51 @@ "description": "Import qr images. Shown as add account method." }, "import_backup_code": { - "message": "Импорт на Текстов архив", + "message": "Импорт на OTPAuth URI", "description": "Import backup code." }, "import_otp_urls": { - "message": "Импорт на OTP URLs", + "message": "Импорт на OTPAuth URI", "description": "Import OTP URLs. Shown as add account method." }, + "import_drop_file": { + "message": "Пуснете архивен файл тук или кликнете за преглед", + "description": "File import dropzone title" + }, + "import_file_accepts": { + "message": "Приема .json и .txt файлове", + "description": "File import dropzone hint" + }, + "import_choose_file": { + "message": "Изберете файл", + "description": "File import dropzone button" + }, + "import_encrypted_note": { + "message": "Ако файлът е криптиран, ще бъдете помолени за паролата му след избирането му.", + "description": "File import note about encrypted backups" + }, + "import_enc_required": { + "message": "Криптиран — изисква се парола", + "description": "Badge on a picked encrypted backup file" + }, + "import_pass_placeholder": { + "message": "Въведете паролата на файла", + "description": "Passphrase input placeholder for encrypted import" + }, + "import_pass_hint": { + "message": "Това е паролата, която сте задали при създаването на резервното копие.", + "description": "Hint under the import passphrase field" + }, + "import_decrypt": { + "message": "Декриптирай и импортирай", + "description": "Button to decrypt and import an encrypted backup" + }, "import_backup_qr_partly_failed": { "message": "Успешен импорт, но някои QR кодове не бяха разпознати.", "description": "Import successful, but some QR image cannot be recognized." }, "import_backup_qr_in_batches": { - "message": "Можете да изберете много файлове за импорт на партиди от резервни копия.", + "message": "Можете да изберете няколко файла, за да импортирате резервни копия на партиди.", "description": "You can select multiple image files to import backup in batches." }, "show_all_entries": { @@ -254,15 +278,15 @@ "description": "Show all entries." }, "dropbox_risk": { - "message": "Предупреждение: архивни файлове са декриптирани. Използвайте на свой собствен риск.", + "message": "Предупреждение: архивни файлове са некриптирани. Използвайте на свой собствен риск.", "description": "Backup risk warning." }, "import_error_password": { - "message": "Трябва да въведете правилна парола, за да импортните архива.", + "message": "Трябва да въведете правилна парола, за да импортирате резервните копия.", "description": "Error password warning when import backups." }, "local_passphrase_warning": { - "message": "Паролата ви се съхранява локално, моля променете го от менюто за сигурност незабавно.", + "message": "Паролата ви се съхранява локално, моля, променете я от менюто за сигурност незабавно.", "description": "localStorage password warning." }, "remove": { @@ -270,7 +294,7 @@ "description": "Remove password." }, "download_enc_backup": { - "message": "Изтегляне на защитени с парола архиви", + "message": "Изтегляне на защитен с парола архив", "description": "Download Encrypted Backup" }, "search": { @@ -278,7 +302,7 @@ "description": "Search" }, "popout": { - "message": "Изскачащ Прозорец", + "message": "Изскачащ прозорец", "description": "Make window turn into persistent popup" }, "lock": { @@ -297,14 +321,106 @@ "message": "Използване на автоматичното попълване", "description": "Use Autofill" }, - "use_high_contrast": { - "message": "Висок контраст", - "description": "Use High Contrast" - }, "theme": { "message": "Тема", "description": "Theme" }, + "settings_appearance": { + "message": "Изглед", + "description": "Settings section: appearance" + }, + "settings_general": { + "message": "Общи", + "description": "Settings section: general" + }, + "edit_accounts": { + "message": "Редактиране на акаунти", + "description": "Header title while in edit mode" + }, + "show_qr": { + "message": "Покажи QR код", + "description": "Context-menu action to show an account's QR code" + }, + "pin_to_top": { + "message": "Закачи най-отгоре", + "description": "Context-menu action to pin an account to the top" + }, + "unpin": { + "message": "Откачи", + "description": "Context-menu action to unpin an account" + }, + "vault_locked": { + "message": "Хранилището е заключено", + "description": "Title on the unlock screen" + }, + "unlock": { + "message": "Отключи", + "description": "Unlock button" + }, + "qr_transfer_title": { + "message": "Прехвърли този акаунт", + "description": "QR sheet caption title" + }, + "qr_transfer_desc": { + "message": "Сканирайте този код с OTPilot на друго устройство, за да прехвърлите този акаунт — той никога не напуска вашите устройства.", + "description": "QR sheet caption body" + }, + "backup_on_device": { + "message": "На това устройство", + "description": "Backup section: local" + }, + "backup_cloud_sync": { + "message": "Облачна синхронизация", + "description": "Backup section: cloud" + }, + "backup_connected": { + "message": "Свързан", + "description": "Cloud provider connected status" + }, + "backup_not_connected": { + "message": "Не е свързан", + "description": "Cloud provider not-connected status" + }, + "showing_codes_for": { + "message": "Показване на кодове за:", + "description": "Smart-filter banner prefix, followed by the site domain" + }, + "other_accounts": { + "message": "Други акаунти", + "description": "Divider above non-matching accounts in the filter view" + }, + "filter_to_site": { + "message": "Филтър към:", + "description": "Banner prefix to re-apply the site filter, followed by the domain" + }, + "onboarding_title": { + "message": "Кодове, които пътуват с вас", + "description": "Onboarding headline" + }, + "onboarding_subtitle": { + "message": "Генерирайте сигурни двуфакторни кодове за всеки акаунт — криптирани, офлайн и винаги на едно докосване от копиране.", + "description": "Onboarding subtitle" + }, + "onboarding_feature_offline": { + "message": "Работи изцяло офлайн — нищо не напуска устройството ви", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_encrypted": { + "message": "Криптирано хранилище с незадължително заключване с парола", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_scan": { + "message": "Сканирайте QR код или импортирайте съществуващо резервно копие", + "description": "Onboarding feature bullet" + }, + "onboarding_get_started": { + "message": "Започнете", + "description": "Onboarding primary button" + }, + "onboarding_import": { + "message": "Имам резервно копие за импортиране", + "description": "Onboarding secondary button" + }, "theme_light": { "message": "Светъл", "description": "Light theme" @@ -313,9 +429,9 @@ "message": "Тъмен", "description": "Dark theme" }, - "theme_simple": { - "message": "Проста", - "description": "Simple theme" + "theme_auto": { + "message": "Автоматично", + "description": "Follow the system light/dark setting" }, "theme_compact": { "message": "Компактна", @@ -325,14 +441,6 @@ "message": "Висок контраст", "description": "High Contrast theme" }, - "theme_flat": { - "message": "Плоска", - "description": "Flat theme" - }, - "storage_sync_info": { - "message": "Автоматично архивиране на вашите данни на външна памет.", - "description": "3rd party backup info" - }, "browser_sync": { "message": "Синхронизация на браузера", "description": "Storage location" @@ -342,18 +450,18 @@ "description": "Sign in to 3rd party storage services" }, "sign_in_business": { - "message": "Вписване (Бизнес)", + "message": "Вход (бизнес)", "description": "Sign in to 3rd party storage services" }, "onedrive_business_perms": { - "message": "Защо за бизнес акаунтите са задължителни повече разрешения?" + "message": "Защо бизнес акаунтите изискват повече разрешения?" }, "log_out": { "message": "Изход", "description": "Sign out of 3rd party storage services" }, "token_revoked": { - "message": "Имаше проблем свързване с вашия $SERVICE$ акаунт, моля влезте отново.", + "message": "Имаше проблем при свързването с вашия $SERVICE$ акаунт, моля влезте отново.", "description": "Error authenticating to backup service. $SERVICE$ will be replaced with a proper noun (E.g.: 'Google Drive' or 'Dropbox')", "placeholders": { "service": { @@ -362,10 +470,6 @@ } } }, - "otp_unsupported_warn": { - "message": "Имате един или повече Steam или Blizzard акаунти. Некриптираните резервни копия няма да използват стандартизиран формат за архивиране.", - "description": "Warning if using account that is not supported by standard backup format." - }, "otp_backup_inform": { "message": "Можете да импортирате резервни копия от други приложения.", "description": "Info text on import page" @@ -407,11 +511,8 @@ "backup": { "message": "Резервно копие" }, - "backup_file_info": { - "message": "Архивирайте данните си във файл." - }, "password_policy_default_hint": { - "message": "Паролата ви не отговоря на стандартите за сигурност на организацията ви. Свържете сес администратора си за повече информация." + "message": "Паролата ви не отговаря на изискванията за сигурност на вашата организация. Свържете се с администратора си за повече информация." }, "advisor": { "message": "Съветник" @@ -429,7 +530,7 @@ "message": "Можете да активирате автоматично попълване на кодове в уебсайтовете." }, "advisor_insight_smart_filter_not_enabled": { - "message": "Активирането на смарт филтър позволява бърз достъп до акаунти." + "message": "Активирането на умния филтър позволява бърз достъп до акаунти." }, "show_all_insights": { "message": "Покажи всички съвети." @@ -453,7 +554,7 @@ "message": "Научете повече" }, "enable_context_menu": { - "message": "Добави в контектното меню" + "message": "Добави в контекстното меню" }, "no_entires": { "message": "Няма акаунти. Добавете първия си акаунт сега." @@ -474,10 +575,10 @@ "message": "Достъп до текущия раздел за сканиране на QR кодове." }, "permission_storage": { - "message": "Достъп до памет на браузер за запаметяване на данни за акаунта." + "message": "Достъп до паметта на браузъра за съхранение на данни за акаунта." }, "permission_identity": { - "message": "Позволи вход в услуги за съхранение на 3-ти страни." + "message": "Позволява вход в услуги за съхранение на трети страни." }, "permission_alarms": { "message": "Позволява задействане на автоматичното заключване." @@ -486,10 +587,10 @@ "message": "Вкарва скриптове в текущия раздел за сканиране на QR кодове и разрешаване на автоматично попълване." }, "permission_clipboard_write": { - "message": "Дава само write-only достъп до клавиатурата с цел копиране на кодове в клипборда, когато кликнете върху акаунта." + "message": "Предоставя достъп само за запис до клипборда, за да копира кодове в него при кликване върху акаунта." }, "permission_context_menus": { - "message": "Добавя Удостоверител в контекстното меню." + "message": "Добавя OTPilot в контекстното меню." }, "permission_sync_clock": { "message": "Позволява синхронизация на часовника с Google." @@ -500,18 +601,6 @@ "permission_dropbox_cannot_revoke": { "message": "Първо трябва да деактивирате архивирането в Dropbox." }, - "permission_drive": { - "message": "Позволява архивиране в Google Drive." - }, - "permission_drive_cannot_revoke": { - "message": "Първо трябва да деактивирате архивирането в Google Drive." - }, - "permission_onedrive": { - "message": "Позволява архивиране в OneDrive." - }, - "permission_onedrive_cannot_revoke": { - "message": "Първо трябва да деактивирате архивирането в OneDrive." - }, "permission_unknown_permission": { "message": "Неизвестно разрешение. Ако виждате това съобщение, изпратете доклад за грешка." }, @@ -519,6 +608,10 @@ "message": "Невярна парола" }, "activate_auto_filter": { - "message": "Предупреждение: Интелигентният филтър търси приблизително съвпадение на името на домейна с акаунта. Преди да въведете код, винаги проверявайте дали сте в правилния уебсайт!" + "message": "Предупреждение: Умният филтър приблизително съпоставя името на домейна с акаунт. Преди да въведете код, винаги проверявайте дали сте на правилния уебсайт!" + }, + "backup_unavailable": { + "message": "Все още не е налично", + "description": "Shown on a cloud provider that is not implemented yet." } } diff --git a/_locales/bn/messages.json b/_locales/bn/messages.json index acef2f20c..c3dbdb7cf 100644 --- a/_locales/bn/messages.json +++ b/_locales/bn/messages.json @@ -1,28 +1,40 @@ { "extName": { - "message": "অথেন্টিকেটর", + "message": "OTPilot Authenticator", "description": "Extension Name." }, "extShortName": { - "message": "অথেন্টিকেটর", + "message": "OTPilot", "description": "Extension Short Name." }, + "host": { + "message": "ওয়েবসাইট", + "description": "Bound website host used to restrict autofill to a matching page." + }, + "backup_requires_password": { + "message": "ক্লাউড ব্যাকআপ ব্যবহার করতে প্রথমে একটি পাসওয়ার্ড সেট করুন, যাতে আপনার সিক্রেট এই ডিভাইস ছাড়ার আগেই এনক্রিপ্ট করা হয়।", + "description": "Shown in the cloud backup section when no master password is set." + }, "extDesc": { - "message": "অথেন্টিকেটর আপনার ব্রাউজারে টু-ফ্যাক্টর অথেন্টিকেশন কোড উৎপন্ন করে।", + "message": "OTPilot Authenticator টু-ফ্যাক্টর (2FA) কোড তৈরি করে, সাথে পাবেন এনক্রিপ্টেড ক্লাউড ব্যাকআপ, হোস্ট মিলিয়ে অটোফিল এবং নিরাপত্তা পরামর্শ।", "description": "Extension Description." }, "added": { - "message": "যোগ করা হয়েছে।", + "message": " যোগ করা হয়েছে।", "description": "Added Account." }, "errorqr": { - "message": "অস্বীকৃত কিউ-আর কোড।", + "message": "অচেনা কিউ-আর কোড।", "description": "QR Error." }, "errorsecret": { "message": "অবৈধ অ্যাকাউন্ট সিক্রেট", "description": "Secret Error." }, + "errorissuer": { + "message": "প্রদানকারীতে \"::\" থাকতে পারবে না", + "description": "Issuer Error." + }, "add_code": { "message": "অ্যাকাউন্ট যোগ করুন", "description": "Add account." @@ -36,17 +48,13 @@ "description": "Manual Entry." }, "migration_fail": { - "message": "আমদানি করতে ব্যর্থ। যদি আপনি গুগল অথেন্টিকেটর থেকে ডেটা স্থানান্তরিত করে থাকেন, অনুগ্রহ করে গুগল অথেন্টিকেটর থেকে আপনার ডেটা পুনঃআমদানি করুন এবং আবার চেষ্টা করুন।", + "message": "আমদানি করতে ব্যর্থ হয়েছে। আপনি যদি গুগল অথেন্টিকেটর থেকে ডেটা স্থানান্তর করছেন, তাহলে অনুগ্রহ করে গুগল অথেন্টিকেটর থেকে আপনার ডেটা পুনরায় এক্সপোর্ট করে আবার চেষ্টা করুন।", "description": "Import migration data failed." }, "migration_partly_fail": { "message": "কিছু অ্যাকাউন্ট ডেটা সফলভাবে আমদানি করা হয়নি।", "description": "Some migration data is broken." }, - "close": { - "message": "বন্ধ করুন", - "description": "Close." - }, "ok": { "message": "ঠিক আছে", "description": "OK." @@ -99,10 +107,6 @@ "message": "চলতি পাসওয়ার্ড", "description": "Current Passphrase." }, - "new_phrase": { - "message": "নতুন পাসওয়ার্ড", - "description": "New Passphrase." - }, "phrase": { "message": "পাসওয়ার্ড", "description": "Passphrase." @@ -120,19 +124,15 @@ "description": "Message that user is required to acknowledge before clearing all data." }, "delete_all": { - "message": "অথেন্টিকেটর রিসেট করুন" + "message": "OTPilot রিসেট করুন" }, "delete_all_warning": { - "message": "এই কাজের ফলে আপনার সকল ডেটা মুছে ফেলা হবে এবং সম্পূর্ণভাবে অথেন্টিকেটর রিসেট করা হবে। আপনি কোনোভাবেই যেকোনো মুছে ফেলা ডেটা পুনরুদ্ধার করতে পারবেন না! অথেন্টিকেটর রিসেট করার পূর্বে ব্যাকআপ ডেটা সংরক্ষণ করার ব্যাপারে আপনার বিবেচনা করা উচিত।" + "message": "এই কাজের ফলে আপনার সকল ডেটা মুছে ফেলা হবে এবং সম্পূর্ণভাবে OTPilot রিসেট করা হবে। আপনি কোনোভাবেই মুছে ফেলা কোনো ডেটা পুনরুদ্ধার করতে পারবেন না! OTPilot রিসেট করার আগে একটি ব্যাকআপ সংরক্ষণ করার কথা বিবেচনা করা উচিত।" }, "security_warning": { "message": "এই পাসওয়ার্ড আপনার অ্যাকাউন্টকে এনক্রিপ্ট করতে কাজে লাগবে। কেউ আপনাকে সাহায্য করবে না যদি আপনি পাসওয়ার্ড ভুলে যান।", "description": "Passphrase Warning." }, - "update": { - "message": "আপডেট", - "description": "Update." - }, "phrase_incorrect": { "message": "যতক্ষণ না পর্যন্ত আপনার অ্যাকাউন্টগুলো ডিক্রিপ্ট (খোলা) হবে ততক্ষণ পর্যন্ত আপনি আর নতুন অ্যাকাউন্ট যোগ করতে পারবেন না। অনুগ্রহ করে চালিয়ে যাওয়ার আগে সঠিক পাসওয়ার্ড প্রবেশ করান।", "description": "Passphrase Incorrect." @@ -157,10 +157,6 @@ "message": "মতামত", "description": "Feedback." }, - "translate": { - "message": "অনুবাদ", - "description": "Translate." - }, "source": { "message": "সোর্স কোড", "description": "Source Code." @@ -170,15 +166,11 @@ "description": "Passphrase Info" }, "sync_clock": { - "message": "গুগলের সাথে ক্লক সুসংগত করুন", + "message": "গুগলের সাথে ঘড়ি সিঙ্ক করুন", "description": "Sync Clock" }, - "remember_phrase": { - "message": "পাসওয়ার্ড মনে রাখুন", - "description": "Remember Passphrase" - }, "clock_too_far_off": { - "message": "সাবধান! আপনার স্থানীয় ঘড়ি অনেক আগে গিয়েছে, অনুগ্রহ করে চালিয়ে যেতে সংশোধন করুন।", + "message": "সাবধান! আপনার স্থানীয় ঘড়ির সময় সঠিক সময় থেকে অনেকটা আলাদা, চালিয়ে যাওয়ার আগে অনুগ্রহ করে তা ঠিক করে নিন।", "description": "Local Time is Too Far Off" }, "remind_backup": { @@ -210,7 +202,7 @@ "description": "Scale" }, "export_info": { - "message": "সতর্কতাঃ সকল ব্যাকআপগুলো এনক্রিপ্টেড নয়। অন্য আরেকটি অ্যাপে একটি অ্যাকাউন্ট যোগ করতে চান? যেকোনো একাউন্টের উপর-ডান অংশে যান আর গোপন বাটনে চাপ দিন।", + "message": "সতর্কতাঃ সকল ব্যাকআপ এনক্রিপ্টেড নয়। অন্য কোনো অ্যাপে একটি অ্যাকাউন্ট যোগ করতে চান? যেকোনো অ্যাকাউন্টের উপর-ডান অংশে গিয়ে লুকানো বাটনে চাপ দিন।", "description": "Export menu info text" }, "download_backup": { @@ -230,19 +222,51 @@ "description": "Import qr image backup." }, "import_qr_images": { - "message": "Import QR Images", + "message": "কিউ-আর ইমেজ আমদানি করুন", "description": "Import qr images. Shown as add account method." }, "import_backup_code": { - "message": "টেক্সট ব্যাকআপ আমদানি করুন", + "message": "OTPAuth URI আমদানি করুন", "description": "Import backup code." }, "import_otp_urls": { - "message": "Import OTP URLs", + "message": "OTPAuth URI আমদানি করুন", "description": "Import OTP URLs. Shown as add account method." }, + "import_drop_file": { + "message": "একটি ব্যাকআপ ফাইল ড্র্যাগ করে ছাড়ুন অথবা ব্রাউজ করতে ক্লিক করুন", + "description": "File import dropzone title" + }, + "import_file_accepts": { + "message": ".json এবং .txt এক্সপোর্ট গ্রহণযোগ্য", + "description": "File import dropzone hint" + }, + "import_choose_file": { + "message": "ফাইল নির্বাচন করুন", + "description": "File import dropzone button" + }, + "import_encrypted_note": { + "message": "ফাইলটি এনক্রিপ্টেড হলে, নির্বাচন করার পর এর পাসওয়ার্ড জিজ্ঞাসা করা হবে।", + "description": "File import note about encrypted backups" + }, + "import_enc_required": { + "message": "এনক্রিপ্টেড — পাসওয়ার্ড প্রয়োজন", + "description": "Badge on a picked encrypted backup file" + }, + "import_pass_placeholder": { + "message": "ফাইলের পাসওয়ার্ড প্রবেশ করান", + "description": "Passphrase input placeholder for encrypted import" + }, + "import_pass_hint": { + "message": "এটি সেই পাসওয়ার্ড যা আপনি ব্যাকআপ তৈরি করার সময় সেট করেছিলেন।", + "description": "Hint under the import passphrase field" + }, + "import_decrypt": { + "message": "ডিক্রিপ্ট করে আমদানি করুন", + "description": "Button to decrypt and import an encrypted backup" + }, "import_backup_qr_partly_failed": { - "message": "সফলভাবে আমদানি করা হয়েছে, কিন্তু কিছু কিউ-আর ইমেজ চেনা যাচ্ছে না।.", + "message": "সফলভাবে আমদানি করা হয়েছে, কিন্তু কিছু কিউ-আর ইমেজ চেনা যাচ্ছে না।", "description": "Import successful, but some QR image cannot be recognized." }, "import_backup_qr_in_batches": { @@ -254,7 +278,7 @@ "description": "Show all entries." }, "dropbox_risk": { - "message": "সাবধানঃ ব্যাকআপগুলো এনক্রিপ্টেড নয়। নিজের ঝুঁকিতে ব্যবহার করুন।", + "message": "সতর্কতাঃ ব্যাকআপ এনক্রিপ্টেড নয়। নিজের ঝুঁকিতে ব্যবহার করুন।", "description": "Backup risk warning." }, "import_error_password": { @@ -297,14 +321,106 @@ "message": "অটোফিল ব্যবহার করুন", "description": "Use Autofill" }, - "use_high_contrast": { - "message": "উচ্চ কন্ট্র্যাস্ট ব্যবহার করুন", - "description": "Use High Contrast" - }, "theme": { "message": "থিম", "description": "Theme" }, + "settings_appearance": { + "message": "চেহারা", + "description": "Settings section: appearance" + }, + "settings_general": { + "message": "সাধারণ", + "description": "Settings section: general" + }, + "edit_accounts": { + "message": "অ্যাকাউন্ট সম্পাদনা করুন", + "description": "Header title while in edit mode" + }, + "show_qr": { + "message": "কিউ-আর কোড দেখান", + "description": "Context-menu action to show an account's QR code" + }, + "pin_to_top": { + "message": "উপরে পিন করুন", + "description": "Context-menu action to pin an account to the top" + }, + "unpin": { + "message": "আনপিন করুন", + "description": "Context-menu action to unpin an account" + }, + "vault_locked": { + "message": "ভল্ট লক করা আছে", + "description": "Title on the unlock screen" + }, + "unlock": { + "message": "আনলক করুন", + "description": "Unlock button" + }, + "qr_transfer_title": { + "message": "এই অ্যাকাউন্ট স্থানান্তর করুন", + "description": "QR sheet caption title" + }, + "qr_transfer_desc": { + "message": "এই অ্যাকাউন্টটি স্থানান্তর করতে অন্য একটি ডিভাইসে OTPilot দিয়ে এই কোডটি স্ক্যান করুন — এটি কখনো আপনার ডিভাইসের বাইরে যায় না।", + "description": "QR sheet caption body" + }, + "backup_on_device": { + "message": "এই ডিভাইসে", + "description": "Backup section: local" + }, + "backup_cloud_sync": { + "message": "ক্লাউড সিঙ্ক", + "description": "Backup section: cloud" + }, + "backup_connected": { + "message": "সংযুক্ত", + "description": "Cloud provider connected status" + }, + "backup_not_connected": { + "message": "সংযুক্ত নয়", + "description": "Cloud provider not-connected status" + }, + "showing_codes_for": { + "message": "এই সাইটের কোড দেখানো হচ্ছে:", + "description": "Smart-filter banner prefix, followed by the site domain" + }, + "other_accounts": { + "message": "অন্যান্য অ্যাকাউন্ট", + "description": "Divider above non-matching accounts in the filter view" + }, + "filter_to_site": { + "message": "শুধু এই সাইট দেখান:", + "description": "Banner prefix to re-apply the site filter, followed by the domain" + }, + "onboarding_title": { + "message": "আপনার সাথে থাকা কোড", + "description": "Onboarding headline" + }, + "onboarding_subtitle": { + "message": "প্রতিটি অ্যাকাউন্টের জন্য নিরাপদ টু-ফ্যাক্টর কোড তৈরি করুন — এনক্রিপ্টেড, অফলাইন এবং সবসময় এক ট্যাপেই কপি করার জন্য প্রস্তুত।", + "description": "Onboarding subtitle" + }, + "onboarding_feature_offline": { + "message": "সম্পূর্ণভাবে অফলাইনে কাজ করে — কিছুই আপনার ডিভাইস ছেড়ে যায় না", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_encrypted": { + "message": "এনক্রিপ্টেড ভল্ট, ঐচ্ছিক পাসওয়ার্ড লক সহ", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_scan": { + "message": "কিউ-আর কোড স্ক্যান করুন অথবা বিদ্যমান ব্যাকআপ আমদানি করুন", + "description": "Onboarding feature bullet" + }, + "onboarding_get_started": { + "message": "শুরু করুন", + "description": "Onboarding primary button" + }, + "onboarding_import": { + "message": "আমার কাছে আমদানি করার মতো একটি ব্যাকআপ আছে", + "description": "Onboarding secondary button" + }, "theme_light": { "message": "লাইট", "description": "Light theme" @@ -313,9 +429,9 @@ "message": "ডার্ক", "description": "Dark theme" }, - "theme_simple": { - "message": "সিম্পল", - "description": "Simple theme" + "theme_auto": { + "message": "স্বয়ংক্রিয়", + "description": "Follow the system light/dark setting" }, "theme_compact": { "message": "কমপ্যাক্ট", @@ -325,14 +441,6 @@ "message": "উচ্চ কন্ট্র্যাস্ট", "description": "High Contrast theme" }, - "theme_flat": { - "message": "Flat", - "description": "Flat theme" - }, - "storage_sync_info": { - "message": "তৃতীয় পক্ষের স্টোরেজ সেবাসমূহে আপনার ডেটা স্বয়ংক্রিয়ভাবে ব্যাকআপ করুন।", - "description": "3rd party backup info" - }, "browser_sync": { "message": "ব্রাউজার সিঙ্ক", "description": "Storage location" @@ -346,7 +454,7 @@ "description": "Sign in to 3rd party storage services" }, "onedrive_business_perms": { - "message": "ব্যবসায়িক একাউন্টগুলোতে বেশি অনুমতির প্রয়োজন পড়ে কেন?" + "message": "ব্যবসায়িক অ্যাকাউন্টগুলোতে বেশি অনুমতির প্রয়োজন পড়ে কেন?" }, "log_out": { "message": "লগআউট করুন", @@ -362,10 +470,6 @@ } } }, - "otp_unsupported_warn": { - "message": "আপনার কাছে এক বা একাধিক স্টিম অথবা ব্লিজার্ড অ্যাকাউন্ট আছে। খোলা বা আনএনক্রিপ্টেড ব্যাকআপগুলো প্রমিত ব্যাকআপ ফরমেট ব্যবহার করবে না।", - "description": "Warning if using account that is not supported by standard backup format." - }, "otp_backup_inform": { "message": "অন্য কিছু অ্যাপ্লিকেশনগুলো থেকে আপনি ব্যাকআপ আমদানি করতে পারবেন।", "description": "Info text on import page" @@ -396,7 +500,7 @@ "message": "অবৈধ" }, "digits": { - "message": "ডিজিটস" + "message": "ডিজিট" }, "algorithm": { "message": "অ্যালগরিদম" @@ -407,9 +511,6 @@ "backup": { "message": "ব্যাকআপ" }, - "backup_file_info": { - "message": "একটি ফাইলে আপনার ডেটা ব্যাকআপ করুন।" - }, "password_policy_default_hint": { "message": "আপনার পাসওয়ার্ড আপনার প্রতিষ্ঠানের নিরাপত্তা প্রয়োজনীয়তায় মিলছে না। আরো তথ্য জানতে আপনার পরিচালকের সাথে যোগাযোগ করুন।" }, @@ -423,10 +524,10 @@ "message": "আপনার ডেটা সুরক্ষিত রাখতে অটো-লক চালু করুন।" }, "advisor_insight_browser_sync_not_enabled": { - "message": "ব্রাউজার সিঙ্ক অচল। চালু করলে আপনার অ্যাকাউন্টগুলো ব্রাউজারজুড়ে সিঙ্ক হবে, যেখানে আপনার ব্রাউজারে এই এক্সটেনশন চালু করা আছে।" + "message": "ব্রাউজার সিঙ্ক বন্ধ আছে। এটি চালু করলে আপনার অ্যাকাউন্টগুলো বিভিন্ন ব্রাউজারে সিঙ্ক করা যাবে।" }, "advisor_insight_auto_fill_not_enabled": { - "message": "ওয়েবসাইটে কোড স্বয়ংক্রিয়ভাবে পূরণ করতে অটোফিল চালু করা যাবে" + "message": "ওয়েবসাইটে কোড স্বয়ংক্রিয়ভাবে পূরণ করতে অটোফিল চালু করা যাবে।" }, "advisor_insight_smart_filter_not_enabled": { "message": "স্মার্ট ফিল্টার চালু করার ফলে অ্যাকাউন্টে কুইক এক্সেস পাওয়া যাবে।" @@ -438,10 +539,10 @@ "message": "কোন অন্তর্দৃষ্টি পাওয়া যায়নি, সবকিছু ভালো দেখা যাচ্ছে!" }, "danger": { - "message": "বিপজ্জনক" + "message": "বিপদ" }, "warning": { - "message": "সাবধান" + "message": "সতর্কতা" }, "info": { "message": "তথ্য" @@ -462,63 +563,55 @@ "message": "অনুমতিসমূহ" }, "permission_revoke": { - "message": "অনুমতি প্রত্যাহার" + "message": "প্রত্যাহার করুন" }, "permission_show_required_permissions": { "message": "প্রয়োজনীয় অনুমতি দেখান" }, "permission_required": { - "message": "এই অনুমতি জরুরি এবং এটা বন্ধ করা যাবে না।." + "message": "এই অনুমতিটি আবশ্যক এবং প্রত্যাহার করা যাবে না।" }, "permission_active_tab": { - "message": "কিউআর কোডটি স্ক্যান করার জন্য বর্তমান ট্যাব এর অ্যাক্সেস করুন।" + "message": "কিউ-আর কোড স্ক্যান করার জন্য বর্তমান ট্যাবে অ্যাক্সেস।" }, "permission_storage": { - "message": "অ্যাকাউন্টের সকল তথ্য সংরক্ষণ এর জন্য ব্রাউজারের স্টোরএজ অ্যাক্সেস করুন।" + "message": "অ্যাকাউন্টের তথ্য সংরক্ষণের জন্য ব্রাউজার স্টোরেজে অ্যাক্সেস।" }, "permission_identity": { - "message": "তৃতীয় পক্ষের ক্লাউড স্টোরএজ সেবায় যুক্ত হওয়ার অনুমতি। " + "message": "তৃতীয় পক্ষের ক্লাউড স্টোরেজ সেবায় সাইন ইন করার অনুমতি দেয়।" }, "permission_alarms": { - "message": "Allows auto-lock to work." + "message": "অটো-লক কাজ করার অনুমতি দেয়।" }, "permission_scripting": { - "message": "Inject scripts into he current tab to scan QR codes and allow auto-fill to work." + "message": "কিউ-আর কোড স্ক্যান করতে ও অটোফিল কাজ করানোর জন্য বর্তমান ট্যাবে স্ক্রিপ্ট প্রবেশ করায়।" }, "permission_clipboard_write": { "message": "আপনি যখন অ্যাকাউন্টে ক্লিক করেন তখন ক্লিপবোর্ডে কোডটি কপি করতে ক্লিপবোর্ডে শুধুমাত্র লেখার অ্যাক্সেস প্রদান করে।" }, "permission_context_menus": { - "message": "কনট্যাক্স মেনু তে অথেন্টিকেটর যুক্ত করুন। " + "message": "কন্টেক্সট মেনুতে OTPilot যুক্ত করে।" }, "permission_sync_clock": { - "message": "গুগল এর সাথে ঘড়ি সিঙ্ক করুন।" + "message": "গুগলের সাথে ঘড়ি সিঙ্ক করার অনুমতি দেয়।" }, "permission_dropbox": { - "message": "ড্রপবক্সের ব্যাকআপ চালু করুন।" + "message": "ড্রপবক্সে ব্যাকআপ নেওয়ার অনুমতি দেয়।" }, "permission_dropbox_cannot_revoke": { - "message": "আপনাকে অবশ্যই ড্রপবক্সের ব্যাকআপ বন্ধ করতে হবে" - }, - "permission_drive": { - "message": "গুগল ড্রাইভের ব্যাকআপ চালু করুন।" - }, - "permission_drive_cannot_revoke": { - "message": "আপনাকে অবশ্যই গুগ্রল ড্রাইভের ব্যাকআপ বন্ধ করতে হবে।" - }, - "permission_onedrive": { - "message": "ওয়ানড্রাইভ এর ব্যাকআপ চালু করুন।" - }, - "permission_onedrive_cannot_revoke": { - "message": "আপনাকে অবশ্যই ওয়ানড্রাইভ এর ব্যাকআপ বন্ধ করতে হবে." + "message": "আপনাকে অবশ্যই প্রথমে ড্রপবক্স ব্যাকআপ বন্ধ করতে হবে।" }, "permission_unknown_permission": { - "message": "অনুমতি অজানা। আপনি যদি এই লেখাটি দেখে থাকেন, তাহলে দয়া করে বাগ রিপোর্ট পাঠান" + "message": "অনুমতি অজানা। আপনি যদি এই বার্তাটি দেখে থাকেন, তাহলে দয়া করে একটি বাগ রিপোর্ট পাঠান।" }, "phrase_wrong": { - "message": "Password incorrect" + "message": "পাসওয়ার্ড ভুল" }, "activate_auto_filter": { - "message": "Warning: Smart filter loosely matches the domain name to an account. Always verify that you are on the correct website before entering a code!" + "message": "সতর্কতাঃ স্মার্ট ফিল্টার আনুমানিকভাবে ডোমেইন নামের সাথে একটি অ্যাকাউন্ট মিলিয়ে দেখায়। কোড প্রবেশ করানোর আগে সবসময় নিশ্চিত করুন যে আপনি সঠিক ওয়েবসাইটে আছেন!" + }, + "backup_unavailable": { + "message": "এখনো উপলব্ধ নয়", + "description": "Shown on a cloud provider that is not implemented yet." } } diff --git a/_locales/ca/messages.json b/_locales/ca/messages.json index 1bc087388..bb9305097 100644 --- a/_locales/ca/messages.json +++ b/_locales/ca/messages.json @@ -1,14 +1,22 @@ { "extName": { - "message": "Authenticator", + "message": "OTPilot Authenticator", "description": "Extension Name." }, "extShortName": { - "message": "Authenticator", + "message": "OTPilot", "description": "Extension Short Name." }, + "host": { + "message": "Lloc web", + "description": "Bound website host used to restrict autofill to a matching page." + }, + "backup_requires_password": { + "message": "Configureu primer una contrasenya per utilitzar la còpia de seguretat al núvol, així els vostres secrets es xifraran abans de sortir d'aquest dispositiu.", + "description": "Shown in the cloud backup section when no master password is set." + }, "extDesc": { - "message": "Authenticator genera codis d'autenticació en dos passos al vostre navegador.", + "message": "OTPilot Authenticator genera codis de dos factors (2FA), amb còpia de seguretat xifrada al núvol, emplenament automàtic vinculat al lloc web i consells de seguretat.", "description": "Extension Description." }, "added": { @@ -23,6 +31,10 @@ "message": "Clau secreta del compte no vàlida", "description": "Secret Error." }, + "errorissuer": { + "message": "L'emissor no pot contenir «::»", + "description": "Issuer Error." + }, "add_code": { "message": "Afegeix compte", "description": "Add account." @@ -43,10 +55,6 @@ "message": "Algunes dades del compte no s'han importat correctament.", "description": "Some migration data is broken." }, - "close": { - "message": "Tanca", - "description": "Close." - }, "ok": { "message": "D'acord", "description": "OK." @@ -99,10 +107,6 @@ "message": "Contrasenya actual", "description": "Current Passphrase." }, - "new_phrase": { - "message": "Nova contrasenya", - "description": "New Passphrase." - }, "phrase": { "message": "Contrasenya", "description": "Passphrase." @@ -120,19 +124,15 @@ "description": "Message that user is required to acknowledge before clearing all data." }, "delete_all": { - "message": "Reinicialitza l'Authenticator" + "message": "Reinicialitza l'OTPilot" }, "delete_all_warning": { - "message": "Això esborrarà totes les vostres dades i reiniciarà completament l'Authenticator. No podreu recuperar cap dada un cop esborrades! Considereu de fer una còpia de seguretat abans de reiniciar l'Authenticator." + "message": "Això esborrarà totes les vostres dades i reiniciarà completament l'OTPilot. No podreu recuperar cap dada un cop esborrades! Considereu de fer una còpia de seguretat abans de reiniciar l'OTPilot." }, "security_warning": { "message": "Aquesta contrasenya s'emprarà per xifrar els vostres comptes. Ningú us podrà ajudar si oblideu la contrasenya.", "description": "Passphrase Warning." }, - "update": { - "message": "Actualitza", - "description": "Update." - }, "phrase_incorrect": { "message": "No podeu afegir un nou compte fins que s'hagin desxifrat tots els comptes. Escriviu la contrasenya correcta abans de continuar.", "description": "Passphrase Incorrect." @@ -154,13 +154,9 @@ "description": "Copied." }, "feedback": { - "message": "Retroacció", + "message": "Comentaris", "description": "Feedback." }, - "translate": { - "message": "Tradueix", - "description": "Translate." - }, "source": { "message": "Codi font", "description": "Source Code." @@ -170,13 +166,9 @@ "description": "Passphrase Info" }, "sync_clock": { - "message": "Sincronitzar el rellotge amb Google", + "message": "Sincronitza el rellotge amb Google", "description": "Sync Clock" }, - "remember_phrase": { - "message": "Recorda la contrasenya", - "description": "Remember Passphrase" - }, "clock_too_far_off": { "message": "Compte! El rellotge del vostre sistema està massa desajustat, solucioneu-ho abans de continuar.", "description": "Local Time is Too Far Off" @@ -190,15 +182,15 @@ "description": "Capture Failed" }, "capture_local_file_failed": { - "message": "Proveu de llegir un codi QR des d'un arxiu local? Enlloc d'això, utilitzeu la importació de còpies de seguretat d'imatges QR.", + "message": "Proveu de llegir un codi QR des d'un fitxer local? En lloc d'això, utilitzeu la importació de còpia d'imatge QR.", "description": "Import QR image backup instead of scan local image" }, "based_on_time": { - "message": "En base al temps", + "message": "Basat en el temps", "description": "Time Based" }, "based_on_counter": { - "message": "En base a un comptador", + "message": "Basat en un comptador", "description": "Counter Based" }, "resize_popup_page": { @@ -230,17 +222,49 @@ "description": "Import qr image backup." }, "import_qr_images": { - "message": "Import QR Images", + "message": "Importa imatges QR", "description": "Import qr images. Shown as add account method." }, "import_backup_code": { - "message": "Importa còpia de seguretat com a text pla", + "message": "Importa URI d'OTPAuth", "description": "Import backup code." }, "import_otp_urls": { - "message": "Import OTP URLs", + "message": "Importa URI d'OTPAuth", "description": "Import OTP URLs. Shown as add account method." }, + "import_drop_file": { + "message": "Deixeu anar un fitxer de còpia de seguretat o feu clic per explorar", + "description": "File import dropzone title" + }, + "import_file_accepts": { + "message": "Accepta exportacions .json i .txt", + "description": "File import dropzone hint" + }, + "import_choose_file": { + "message": "Tria un fitxer", + "description": "File import dropzone button" + }, + "import_encrypted_note": { + "message": "Si el fitxer està xifrat, se us demanarà la contrasenya després de seleccionar-lo.", + "description": "File import note about encrypted backups" + }, + "import_enc_required": { + "message": "Xifrat — cal contrasenya", + "description": "Badge on a picked encrypted backup file" + }, + "import_pass_placeholder": { + "message": "Introduïu la contrasenya del fitxer", + "description": "Passphrase input placeholder for encrypted import" + }, + "import_pass_hint": { + "message": "Aquesta és la contrasenya que vau establir en crear la còpia de seguretat.", + "description": "Hint under the import passphrase field" + }, + "import_decrypt": { + "message": "Desxifra i importa", + "description": "Button to decrypt and import an encrypted backup" + }, "import_backup_qr_partly_failed": { "message": "S'ha importat correctament, però alguns codis QR no s'han reconegut.", "description": "Import successful, but some QR image cannot be recognized." @@ -254,7 +278,7 @@ "description": "Show all entries." }, "dropbox_risk": { - "message": "Atenció: tingueu en consideració que les còpies de seguretat no estan xifrades.", + "message": "Atenció: les còpies de seguretat no estan xifrades. Utilitzeu-les sota la vostra responsabilitat.", "description": "Backup risk warning." }, "import_error_password": { @@ -297,14 +321,106 @@ "message": "Utilitza l'emplenament automàtic", "description": "Use Autofill" }, - "use_high_contrast": { - "message": "Contrast alt", - "description": "Use High Contrast" - }, "theme": { "message": "Tema", "description": "Theme" }, + "settings_appearance": { + "message": "Aparença", + "description": "Settings section: appearance" + }, + "settings_general": { + "message": "General", + "description": "Settings section: general" + }, + "edit_accounts": { + "message": "Edita els comptes", + "description": "Header title while in edit mode" + }, + "show_qr": { + "message": "Mostra el codi QR", + "description": "Context-menu action to show an account's QR code" + }, + "pin_to_top": { + "message": "Fixa a dalt", + "description": "Context-menu action to pin an account to the top" + }, + "unpin": { + "message": "Deixa de fixar", + "description": "Context-menu action to unpin an account" + }, + "vault_locked": { + "message": "Cofre bloquejat", + "description": "Title on the unlock screen" + }, + "unlock": { + "message": "Desbloqueja", + "description": "Unlock button" + }, + "qr_transfer_title": { + "message": "Transfereix aquest compte", + "description": "QR sheet caption title" + }, + "qr_transfer_desc": { + "message": "Escanegeu aquest codi amb l'OTPilot en un altre dispositiu per moure aquest compte — mai surt dels vostres dispositius.", + "description": "QR sheet caption body" + }, + "backup_on_device": { + "message": "En aquest dispositiu", + "description": "Backup section: local" + }, + "backup_cloud_sync": { + "message": "Sincronització al núvol", + "description": "Backup section: cloud" + }, + "backup_connected": { + "message": "Connectat", + "description": "Cloud provider connected status" + }, + "backup_not_connected": { + "message": "No connectat", + "description": "Cloud provider not-connected status" + }, + "showing_codes_for": { + "message": "Mostrant codis per a", + "description": "Smart-filter banner prefix, followed by the site domain" + }, + "other_accounts": { + "message": "Altres comptes", + "description": "Divider above non-matching accounts in the filter view" + }, + "filter_to_site": { + "message": "Filtra a", + "description": "Banner prefix to re-apply the site filter, followed by the domain" + }, + "onboarding_title": { + "message": "Codis que us acompanyen", + "description": "Onboarding headline" + }, + "onboarding_subtitle": { + "message": "Genereu codis de dos factors segurs per a cada compte — xifrats, sense connexió i sempre a un toc de copiar.", + "description": "Onboarding subtitle" + }, + "onboarding_feature_offline": { + "message": "Funciona totalment sense connexió — res surt del vostre dispositiu", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_encrypted": { + "message": "Cofre xifrat amb bloqueig per contrasenya opcional", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_scan": { + "message": "Escanegeu un codi QR o importeu una còpia de seguretat existent", + "description": "Onboarding feature bullet" + }, + "onboarding_get_started": { + "message": "Comença", + "description": "Onboarding primary button" + }, + "onboarding_import": { + "message": "Tinc una còpia de seguretat per importar", + "description": "Onboarding secondary button" + }, "theme_light": { "message": "Clar", "description": "Light theme" @@ -313,9 +429,9 @@ "message": "Fosc", "description": "Dark theme" }, - "theme_simple": { - "message": "Senzill", - "description": "Simple theme" + "theme_auto": { + "message": "Automàtic", + "description": "Follow the system light/dark setting" }, "theme_compact": { "message": "Compacte", @@ -325,16 +441,8 @@ "message": "Alt contrast", "description": "High Contrast theme" }, - "theme_flat": { - "message": "Planer", - "description": "Flat theme" - }, - "storage_sync_info": { - "message": "Fes una còpia de seguretat automàtica de les dades a un servei d'emmagatzematge de tercers.", - "description": "3rd party backup info" - }, "browser_sync": { - "message": "Sincronització amb el Navegador", + "message": "Sincronització amb el navegador", "description": "Storage location" }, "sign_in": { @@ -342,14 +450,14 @@ "description": "Sign in to 3rd party storage services" }, "sign_in_business": { - "message": "Inicia sessió (Negocis)", + "message": "Inicia sessió (Empresa)", "description": "Sign in to 3rd party storage services" }, "onedrive_business_perms": { - "message": "Perquè els comptes de negocis demanen més permisos?" + "message": "Per què els comptes d'empresa requereixen més permisos?" }, "log_out": { - "message": "Desconnecta", + "message": "Tanca la sessió", "description": "Sign out of 3rd party storage services" }, "token_revoked": { @@ -362,10 +470,6 @@ } } }, - "otp_unsupported_warn": { - "message": "Teniu un o més comptes de Steam o de Blizzard. Les còpies de seguretat sense xifrar no utilitzaran un format estàndard de còpia de seguretat.", - "description": "Warning if using account that is not supported by standard backup format." - }, "otp_backup_inform": { "message": "Podeu importar còpies de seguretat d'altres aplicacions.", "description": "Info text on import page" @@ -387,7 +491,7 @@ "message": "Avançat" }, "period": { - "message": "període" + "message": "Període" }, "type": { "message": "Tipus" @@ -407,11 +511,8 @@ "backup": { "message": "Còpia de seguretat" }, - "backup_file_info": { - "message": "Fes una còpia de les dades a un fitxer." - }, "password_policy_default_hint": { - "message": "La contrasenya no compleix els requeriments de seguretat de la teva companyia. Contacta amb el teu administrador per a més informació." + "message": "La contrasenya no compleix els requisits de seguretat de la vostra organització. Contacteu amb el vostre administrador per obtenir-ne més informació." }, "advisor": { "message": "Assessor" @@ -423,7 +524,7 @@ "message": "Activeu el bloqueig automàtic per protegir les vostres dades." }, "advisor_insight_browser_sync_not_enabled": { - "message": "La sincronització està desactivada. Habilitar-la permet que els comptes es sincronitzin entre navegadors." + "message": "La sincronització del navegador està desactivada. Activar-la permet sincronitzar els comptes entre navegadors." }, "advisor_insight_auto_fill_not_enabled": { "message": "Podeu activar l'emplenament automàtic per omplir els codis a les pàgines web." @@ -432,10 +533,10 @@ "message": "Activar el filtre intel·ligent permet l'accés ràpid als comptes." }, "show_all_insights": { - "message": "Mostra tots els punts de vista." + "message": "Mostra tots els consells." }, "no_insight_available": { - "message": "No s'han trobat punts de vista, tot sembla correcte!" + "message": "No s'ha trobat cap consell, tot sembla correcte!" }, "danger": { "message": "Perill" @@ -456,7 +557,7 @@ "message": "Afegeix al menú contextual" }, "no_entires": { - "message": "Sense comptes per mostrar. Afegiu un compte ara." + "message": "Sense comptes per mostrar. Afegiu el vostre primer compte ara." }, "permissions": { "message": "Permisos" @@ -480,16 +581,16 @@ "message": "Permet iniciar sessió en serveis d'emmagatzematge de tercers." }, "permission_alarms": { - "message": "Allows auto-lock to work." + "message": "Permet que funcioni el bloqueig automàtic." }, "permission_scripting": { - "message": "Inject scripts into he current tab to scan QR codes and allow auto-fill to work." + "message": "Injecta scripts a la pestanya actual per escanejar codis QR i permetre que funcioni l'emplenament automàtic." }, "permission_clipboard_write": { "message": "Atorga accés només d'escriptura al porta-retalls per copiar codis al porta-retalls quan feu clic al compte." }, "permission_context_menus": { - "message": "Afegeix Authenticator al menú contextual." + "message": "Afegeix OTPilot al menú contextual." }, "permission_sync_clock": { "message": "Permet la sincronització del rellotge amb Google." @@ -500,25 +601,17 @@ "permission_dropbox_cannot_revoke": { "message": "Primer heu de desactivar la còpia de seguretat a Dropbox." }, - "permission_drive": { - "message": "Permet còpies de seguretat a Google Drive." - }, - "permission_drive_cannot_revoke": { - "message": "Primer heu de desactivar la còpia de seguretat a Google Drive." - }, - "permission_onedrive": { - "message": "Permet còpies de seguretat a OneDrive." - }, - "permission_onedrive_cannot_revoke": { - "message": "Primer heu de desactivar la còpia de seguretat a OneDrive." - }, "permission_unknown_permission": { "message": "Permís desconegut. Si veieu aquest missatge, envieu un informe d'error." }, "phrase_wrong": { - "message": "Password incorrect" + "message": "Contrasenya incorrecta" }, "activate_auto_filter": { - "message": "Warning: Smart filter loosely matches the domain name to an account. Always verify that you are on the correct website before entering a code!" + "message": "Atenció: el filtre intel·ligent relaciona el domini amb un compte de manera aproximada. Comproveu sempre que sou al lloc web correcte abans d'introduir un codi!" + }, + "backup_unavailable": { + "message": "Encara no disponible", + "description": "Shown on a cloud provider that is not implemented yet." } } diff --git a/_locales/cs/messages.json b/_locales/cs/messages.json index d944e99cb..7e64c45df 100644 --- a/_locales/cs/messages.json +++ b/_locales/cs/messages.json @@ -1,14 +1,22 @@ { "extName": { - "message": "Authenticator", + "message": "OTPilot Authenticator", "description": "Extension Name." }, "extShortName": { - "message": "Authenticator", + "message": "OTPilot", "description": "Extension Short Name." }, + "host": { + "message": "Web", + "description": "Bound website host used to restrict autofill to a matching page." + }, + "backup_requires_password": { + "message": "Pro použití cloudové zálohy nejprve nastavte heslo, aby byly vaše tajné klíče zašifrovány ještě předtím, než opustí toto zařízení.", + "description": "Shown in the cloud backup section when no master password is set." + }, "extDesc": { - "message": "Authenticator generuje kódy dvoufázového ověření ve Vašem prohlížeči.", + "message": "OTPilot Authenticator generuje kódy dvoufázového ověření (2FA) a nabízí šifrovanou cloudovou zálohu, automatické vyplňování vázané na konkrétní web a bezpečnostní doporučení.", "description": "Extension Description." }, "added": { @@ -20,9 +28,13 @@ "description": "QR Error." }, "errorsecret": { - "message": "Neplatné číslo účtu", + "message": "Neplatný tajný klíč účtu", "description": "Secret Error." }, + "errorissuer": { + "message": "Vydavatel nesmí obsahovat „::“", + "description": "Issuer Error." + }, "add_code": { "message": "Přidat účet", "description": "Add account." @@ -43,10 +55,6 @@ "message": "Některá data účtu nebyla úspěšně importována.", "description": "Some migration data is broken." }, - "close": { - "message": "Zavřít", - "description": "Close." - }, "ok": { "message": "Ok", "description": "OK." @@ -64,7 +72,7 @@ "description": "Account." }, "accountName": { - "message": "Název účtu", + "message": "Uživatelské jméno", "description": "Account Name." }, "issuer": { @@ -72,7 +80,7 @@ "description": "Issuer." }, "secret": { - "message": "Secret", + "message": "Tajný klíč", "description": "Secret." }, "updateSuccess": { @@ -80,7 +88,7 @@ "description": "Update Success." }, "updateFailure": { - "message": "Došlo k chybě.", + "message": "Selhání.", "description": "Update Failure." }, "about": { @@ -99,10 +107,6 @@ "message": "Stávající heslo", "description": "Current Passphrase." }, - "new_phrase": { - "message": "Nové heslo", - "description": "New Passphrase." - }, "phrase": { "message": "Heslo", "description": "Passphrase." @@ -120,25 +124,21 @@ "description": "Message that user is required to acknowledge before clearing all data." }, "delete_all": { - "message": "Resetovat autentifikátor" + "message": "Resetovat OTPilot" }, "delete_all_warning": { - "message": "Tímto smažete všechna vaše data a kompletně obnovíte autentifikátor. Nebudete moci obnovit žádná odstraněná data! Před resetováním autentifikátoru byste měli zvážit uložení zálohy." + "message": "Tímto smažete všechna svá data a kompletně resetujete aplikaci OTPilot. Žádná odstraněná data nebude možné obnovit! Před resetováním aplikace OTPilot byste měli zvážit uložení zálohy." }, "security_warning": { "message": "Toto heslo bude použito k šifrování vašich účtů. Nikdo vám nemůže pomoci, pokud zapomenete heslo.", "description": "Passphrase Warning." }, - "update": { - "message": "Aktualizovat", - "description": "Update." - }, "phrase_incorrect": { "message": "Nelze přidat nový účet, dokud nejsou všechny účty dešifrovány. Před pokračováním zadejte prosím správné heslo.", "description": "Passphrase Incorrect." }, "phrase_incorrect_export": { - "message": "Účty které nebylo možno dešifrovat nebudou zahrnuty v této záloze.", + "message": "Účty, které nebylo možné dešifrovat, nebudou zahrnuty v této záloze.", "description": "Skip Unable-decripted Data." }, "phrase_not_match": { @@ -157,10 +157,6 @@ "message": "Zpětná vazba", "description": "Feedback." }, - "translate": { - "message": "Přeložit", - "description": "Translate." - }, "source": { "message": "Zdrojový kód", "description": "Source Code." @@ -173,16 +169,12 @@ "message": "Synchronizovat hodiny s Google", "description": "Sync Clock" }, - "remember_phrase": { - "message": "Zapamatovat si heslo", - "description": "Remember Passphrase" - }, "clock_too_far_off": { "message": "Pozor! Váš místní čas se velmi liší, před pokračováním jej prosím opravte.", "description": "Local Time is Too Far Off" }, "remind_backup": { - "message": "Máte zálohu vašich účtů? Nečekejte než je příliš pozdě!", + "message": "Máte zálohu svých účtů? Nečekejte, až bude příliš pozdě!", "description": "Remind Backup" }, "capture_failed": { @@ -190,7 +182,7 @@ "description": "Capture Failed" }, "capture_local_file_failed": { - "message": "Snažíte se naskenovat QR kód z místního souboru? Použijte raději Import QR Image Backup.", + "message": "Snažíte se naskenovat QR kód z místního souboru? Použijte raději možnost „Importovat zálohu z QR obrázku“.", "description": "Import QR image backup instead of scan local image" }, "based_on_time": { @@ -198,11 +190,11 @@ "description": "Time Based" }, "based_on_counter": { - "message": "Založeno na počítadle", + "message": "Na základě počítadla", "description": "Counter Based" }, "resize_popup_page": { - "message": "Vyskakovací stránka", + "message": "Předvolby", "description": "Popup Page Settings" }, "scale": { @@ -210,7 +202,7 @@ "description": "Scale" }, "export_info": { - "message": "Upozornění: Všechny zálohy jsou nešifrované. Chcete přidat účet do jiné aplikace? Najeďte na pravou horní část libovolného účtu a stiskněte na skryté tlačítko.", + "message": "Upozornění: všechny zálohy jsou nešifrované. Chcete přidat účet do jiné aplikace? Najeďte myší na pravou horní část libovolného účtu a klikněte na skryté tlačítko.", "description": "Export menu info text" }, "download_backup": { @@ -226,7 +218,7 @@ "description": "Import backup file." }, "import_backup_qr": { - "message": "Import QR Image Backup", + "message": "Importovat zálohu z QR obrázku", "description": "Import qr image backup." }, "import_qr_images": { @@ -234,13 +226,45 @@ "description": "Import qr images. Shown as add account method." }, "import_backup_code": { - "message": "Importovat textovou zálohu", + "message": "Importovat OTPAuth URI", "description": "Import backup code." }, "import_otp_urls": { - "message": "Importovat OTP URL", + "message": "Importovat OTPAuth URI", "description": "Import OTP URLs. Shown as add account method." }, + "import_drop_file": { + "message": "Přetáhněte záložní soubor nebo klikněte pro procházení", + "description": "File import dropzone title" + }, + "import_file_accepts": { + "message": "Podporuje exporty .json a .txt", + "description": "File import dropzone hint" + }, + "import_choose_file": { + "message": "Vybrat soubor", + "description": "File import dropzone button" + }, + "import_encrypted_note": { + "message": "Pokud je soubor šifrovaný, po jeho výběru budete požádáni o heslo.", + "description": "File import note about encrypted backups" + }, + "import_enc_required": { + "message": "Šifrováno — vyžadováno heslo", + "description": "Badge on a picked encrypted backup file" + }, + "import_pass_placeholder": { + "message": "Zadejte heslo k souboru", + "description": "Passphrase input placeholder for encrypted import" + }, + "import_pass_hint": { + "message": "Toto je heslo, které jste nastavili při vytváření zálohy.", + "description": "Hint under the import passphrase field" + }, + "import_decrypt": { + "message": "Dešifrovat a importovat", + "description": "Button to decrypt and import an encrypted backup" + }, "import_backup_qr_partly_failed": { "message": "Import proběhl úspěšně, ale některé kódy QR se nepodařilo rozpoznat.", "description": "Import successful, but some QR image cannot be recognized." @@ -250,7 +274,7 @@ "description": "You can select multiple image files to import backup in batches." }, "show_all_entries": { - "message": "Show all entries", + "message": "Zobrazit všechny položky", "description": "Show all entries." }, "dropbox_risk": { @@ -262,7 +286,7 @@ "description": "Error password warning when import backups." }, "local_passphrase_warning": { - "message": "Vaše heslo je lokálně uloženo, prosím změňte to ihned v menu zabezpečení.", + "message": "Vaše heslo je uloženo lokálně, ihned je prosím změňte v sekci Zabezpečení.", "description": "localStorage password warning." }, "remove": { @@ -270,7 +294,7 @@ "description": "Remove password." }, "download_enc_backup": { - "message": "Stáhnout zálohy chráněné heslem", + "message": "Stáhnout zálohu chráněnou heslem", "description": "Download Encrypted Backup" }, "search": { @@ -278,11 +302,11 @@ "description": "Search" }, "popout": { - "message": "Popup mód", + "message": "Popup režim", "description": "Make window turn into persistent popup" }, "lock": { - "message": "Zámek", + "message": "Zamknout", "description": "Lock accounts" }, "edit": { @@ -294,17 +318,109 @@ "description": "Manual backup" }, "use_autofill": { - "message": "Použití automatického vyplňování", + "message": "Používat automatické vyplňování", "description": "Use Autofill" }, - "use_high_contrast": { - "message": "Použít vysoký kontrast", - "description": "Use High Contrast" - }, "theme": { "message": "Motiv", "description": "Theme" }, + "settings_appearance": { + "message": "Vzhled", + "description": "Settings section: appearance" + }, + "settings_general": { + "message": "Obecné", + "description": "Settings section: general" + }, + "edit_accounts": { + "message": "Upravit účty", + "description": "Header title while in edit mode" + }, + "show_qr": { + "message": "Zobrazit QR kód", + "description": "Context-menu action to show an account's QR code" + }, + "pin_to_top": { + "message": "Připnout nahoru", + "description": "Context-menu action to pin an account to the top" + }, + "unpin": { + "message": "Odepnout", + "description": "Context-menu action to unpin an account" + }, + "vault_locked": { + "message": "Trezor uzamčen", + "description": "Title on the unlock screen" + }, + "unlock": { + "message": "Odemknout", + "description": "Unlock button" + }, + "qr_transfer_title": { + "message": "Přenést tento účet", + "description": "QR sheet caption title" + }, + "qr_transfer_desc": { + "message": "Naskenujte tento kód pomocí OTPilot na jiném zařízení a přeneste tento účet — nikdy neopustí vaše zařízení.", + "description": "QR sheet caption body" + }, + "backup_on_device": { + "message": "Na tomto zařízení", + "description": "Backup section: local" + }, + "backup_cloud_sync": { + "message": "Cloudová synchronizace", + "description": "Backup section: cloud" + }, + "backup_connected": { + "message": "Připojeno", + "description": "Cloud provider connected status" + }, + "backup_not_connected": { + "message": "Nepřipojeno", + "description": "Cloud provider not-connected status" + }, + "showing_codes_for": { + "message": "Zobrazují se kódy pro", + "description": "Smart-filter banner prefix, followed by the site domain" + }, + "other_accounts": { + "message": "Ostatní účty", + "description": "Divider above non-matching accounts in the filter view" + }, + "filter_to_site": { + "message": "Filtrovat na", + "description": "Banner prefix to re-apply the site filter, followed by the domain" + }, + "onboarding_title": { + "message": "Kódy, které jsou vždy s vámi", + "description": "Onboarding headline" + }, + "onboarding_subtitle": { + "message": "Generujte bezpečné dvoufaktorové kódy pro každý účet — šifrované, offline a vždy jen jedno klepnutí od zkopírování.", + "description": "Onboarding subtitle" + }, + "onboarding_feature_offline": { + "message": "Funguje zcela offline — nic neopustí vaše zařízení", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_encrypted": { + "message": "Šifrovaný trezor s volitelným zámkem heslem", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_scan": { + "message": "Naskenujte QR kód nebo importujte existující zálohu", + "description": "Onboarding feature bullet" + }, + "onboarding_get_started": { + "message": "Začít", + "description": "Onboarding primary button" + }, + "onboarding_import": { + "message": "Mám zálohu k importu", + "description": "Onboarding secondary button" + }, "theme_light": { "message": "Světlý", "description": "Light theme" @@ -313,28 +429,20 @@ "message": "Tmavý", "description": "Dark theme" }, - "theme_simple": { - "message": "Jednoduché", - "description": "Simple theme" + "theme_auto": { + "message": "Automaticky", + "description": "Follow the system light/dark setting" }, "theme_compact": { - "message": "Krátký", + "message": "Kompaktní", "description": "Compact theme" }, "theme_high_contrast": { "message": "Vysoký kontrast", "description": "High Contrast theme" }, - "theme_flat": { - "message": "Ploché", - "description": "Flat theme" - }, - "storage_sync_info": { - "message": "Automatically backup your data to 3rd party storage services.", - "description": "3rd party backup info" - }, "browser_sync": { - "message": "Browser Sync", + "message": "Synchronizace prohlížeče", "description": "Storage location" }, "sign_in": { @@ -342,11 +450,11 @@ "description": "Sign in to 3rd party storage services" }, "sign_in_business": { - "message": "Sign in (Business)", + "message": "Přihlásit se (firemní účet)", "description": "Sign in to 3rd party storage services" }, "onedrive_business_perms": { - "message": "Why do business accounts require more permissions?" + "message": "Proč firemní účty vyžadují více oprávnění?" }, "log_out": { "message": "Odhlásit se", @@ -362,16 +470,12 @@ } } }, - "otp_unsupported_warn": { - "message": "Máte jeden nebo více Steam nebo Blizzard účtů. Nešifrované zálohy nebudou využívat standardní formát.", - "description": "Warning if using account that is not supported by standard backup format." - }, "otp_backup_inform": { - "message": "You can import backups from some other applications.", + "message": "Zálohy můžete importovat i z některých dalších aplikací.", "description": "Info text on import page" }, "otp_backup_learn": { - "message": "Číst více", + "message": "Zjistit více", "description": "learn more link on import page. Placed after otp_backup_inform" }, "loading": { @@ -387,7 +491,7 @@ "message": "Pokročilé" }, "period": { - "message": "Sekunda" + "message": "Perioda" }, "type": { "message": "Typ" @@ -396,7 +500,7 @@ "message": "Neplatné" }, "digits": { - "message": "Klávesa" + "message": "Číslice" }, "algorithm": { "message": "Algoritmus" @@ -407,9 +511,6 @@ "backup": { "message": "Záloha" }, - "backup_file_info": { - "message": "Zálohovat data do souboru." - }, "password_policy_default_hint": { "message": "Vaše heslo nesplňuje bezpečnostní požadavky vaší organizace. Pro více informací kontaktujte svého správce." }, @@ -420,22 +521,22 @@ "message": "Nastavte si heslo pro ochranu vašich dat." }, "advisor_insight_auto_lock_not_set": { - "message": "Enable auto-lock to protect your data." + "message": "Povolte automatické zamykání pro ochranu vašich dat." }, "advisor_insight_browser_sync_not_enabled": { - "message": "Synchronizace prohlížeče je vypnuta. Zapnutím zapnete synchronizaci účtu napříč prohlížeči." + "message": "Synchronizace prohlížeče je vypnuta. Jejím zapnutím umožníte synchronizaci účtů napříč prohlížeči." }, "advisor_insight_auto_fill_not_enabled": { - "message": "Autofill can be enabled to automatically fill codes into websites." + "message": "Povolte automatické vyplňování, aby se kódy na webových stránkách vyplňovaly automaticky." }, "advisor_insight_smart_filter_not_enabled": { - "message": "Povolení smart filtru umožňuje rychlý přístup k účtům." + "message": "Povolení chytrého filtru umožňuje rychlý přístup k účtům." }, "show_all_insights": { - "message": "Zobrazit všechny náhledy." + "message": "Zobrazit všechna doporučení." }, "no_insight_available": { - "message": "Nebyly nalezeny žádné náhledy, vše vypadá dobře!" + "message": "Nebyla nalezena žádná doporučení, vše vypadá dobře!" }, "danger": { "message": "Nebezpečí" @@ -462,13 +563,13 @@ "message": "Oprávnění" }, "permission_revoke": { - "message": "Odvoláno" + "message": "Odvolat" }, "permission_show_required_permissions": { "message": "Zobrazit neodvolatelná oprávnění" }, "permission_required": { - "message": "Tato složka je vyžadována systémem, proto nemůže být odstraněna." + "message": "Toto oprávnění je povinné a nelze je odvolat." }, "permission_active_tab": { "message": "Přístup k aktuální záložce pro skenování QR kódů." @@ -489,7 +590,7 @@ "message": "Umožňuje přístup do schránky pouze pro zápis a kopírování kódů do schránky po kliknutí na účet." }, "permission_context_menus": { - "message": "Přidá Authenticator do kontextové nabídky." + "message": "Přidá OTPilot do kontextové nabídky." }, "permission_sync_clock": { "message": "Umožnit synchronizaci hodin s Google." @@ -500,18 +601,6 @@ "permission_dropbox_cannot_revoke": { "message": "Nejdříve musíte zakázat zálohu Dropboxu." }, - "permission_drive": { - "message": "Umožňuje zálohování na Disk Google." - }, - "permission_drive_cannot_revoke": { - "message": "Nejdříve musíte zakázat zálohu Google drive." - }, - "permission_onedrive": { - "message": "Umožňuje zálohu do One drive." - }, - "permission_onedrive_cannot_revoke": { - "message": "Nejdříve musíte zakázat zálohu One drive." - }, "permission_unknown_permission": { "message": "Neznámé oprávnění. Pokud uvidíte tuto zprávu, pošlete prosím hlášení o chybě." }, @@ -519,6 +608,10 @@ "message": "Neplatné heslo" }, "activate_auto_filter": { - "message": "Varování: Inteligentní filtr volně přiřazuje název domény k účtu. Před zadáním kódu vždy zkontrolujte, zda jste na správné webové stránce!" + "message": "Varování: Chytrý filtr volně přiřazuje název domény k účtu. Před zadáním kódu vždy zkontrolujte, zda jste na správné webové stránce!" + }, + "backup_unavailable": { + "message": "Zatím není k dispozici", + "description": "Shown on a cloud provider that is not implemented yet." } } diff --git a/_locales/da/messages.json b/_locales/da/messages.json index d7974aa39..bff489d9e 100644 --- a/_locales/da/messages.json +++ b/_locales/da/messages.json @@ -1,14 +1,22 @@ { "extName": { - "message": "Autentificering", + "message": "OTPilot Authenticator", "description": "Extension Name." }, "extShortName": { - "message": "Autentificering", + "message": "OTPilot", "description": "Extension Short Name." }, + "host": { + "message": "Websted", + "description": "Bound website host used to restrict autofill to a matching page." + }, + "backup_requires_password": { + "message": "Angiv en adgangskode først for at bruge cloud-sikkerhedskopiering, så dine hemmeligheder krypteres, før de forlader denne enhed.", + "description": "Shown in the cloud backup section when no master password is set." + }, "extDesc": { - "message": "Authenticator genererer to-faktor autentificeringskoder i din browser.", + "message": "OTPilot Authenticator genererer tofaktorkoder (2FA) med krypteret cloud-sikkerhedskopiering, host-matchet autofyld og sikkerhedsråd.", "description": "Extension Description." }, "added": { @@ -23,8 +31,12 @@ "message": "Ugyldig kontohemmelighed", "description": "Secret Error." }, + "errorissuer": { + "message": "Udsteder må ikke indeholde \"::\"", + "description": "Issuer Error." + }, "add_code": { - "message": "Tilføj konti", + "message": "Tilføj konto", "description": "Add account." }, "add_qr": { @@ -36,17 +48,13 @@ "description": "Manual Entry." }, "migration_fail": { - "message": "Import mislykkedes. Hvis du migrerer data fra Google Authenticator, skal du geneksportere dine data fra Google Authenticator og prøve igen.", + "message": "Import mislykkedes. Hvis du migrerer data fra Google Authenticator, skal du eksportere dine data fra Google Authenticator på ny og prøve igen.", "description": "Import migration data failed." }, "migration_partly_fail": { "message": "Nogle kontodata blev ikke importeret.", "description": "Some migration data is broken." }, - "close": { - "message": "Luk", - "description": "Close." - }, "ok": { "message": "Ok", "description": "OK." @@ -68,7 +76,7 @@ "description": "Account Name." }, "issuer": { - "message": "Udstederen", + "message": "Udsteder", "description": "Issuer." }, "secret": { @@ -96,13 +104,9 @@ "description": "Security." }, "current_phrase": { - "message": "Nuværende Adgangskode", + "message": "Nuværende adgangskode", "description": "Current Passphrase." }, - "new_phrase": { - "message": "Ny adgangskode", - "description": "New Passphrase." - }, "phrase": { "message": "Adgangskode", "description": "Passphrase." @@ -120,29 +124,25 @@ "description": "Message that user is required to acknowledge before clearing all data." }, "delete_all": { - "message": "Nulstil Autentificering" + "message": "Nulstil OTPilot" }, "delete_all_warning": { - "message": "Dette vil slette alle dine data og helt nulstille Authenticator. Du vil ikke være i stand til at gendanne slettede data! Du bør overveje at gemme en sikkerhedskopi, før du nulstiller Authenticator." + "message": "Dette vil slette alle dine data og helt nulstille OTPilot. Du vil ikke være i stand til at gendanne slettede data! Du bør overveje at gemme en sikkerhedskopi, før du nulstiller OTPilot." }, "security_warning": { "message": "Denne adgangskode bruges til at kryptere dine konti. Ingen kan hjælpe dig, hvis du glemmer adgangskoden.", "description": "Passphrase Warning." }, - "update": { - "message": "Opdatér", - "description": "Update." - }, "phrase_incorrect": { "message": "Du kan ikke tilføje en ny konto før alle konti er dekrypteret. Indtast den korrekte adgangskode, før du fortsætter.", "description": "Passphrase Incorrect." }, "phrase_incorrect_export": { - "message": "Konti, der ikke var i stand til at blive dekrypteret vil ikke blive inkluderet i denne backup.", + "message": "Konti, der ikke kunne dekrypteres, vil ikke blive inkluderet i denne sikkerhedskopi.", "description": "Skip Unable-decripted Data." }, "phrase_not_match": { - "message": "Kodeord passer ikke.", + "message": "Adgangskoden stemmer ikke overens.", "description": "Passphrase Not Match." }, "encrypted": { @@ -157,10 +157,6 @@ "message": "Feedback", "description": "Feedback." }, - "translate": { - "message": "Oversæt", - "description": "Translate." - }, "source": { "message": "Kildekode", "description": "Source Code." @@ -170,15 +166,11 @@ "description": "Passphrase Info" }, "sync_clock": { - "message": "Synk. historik med Google", + "message": "Synkroniser ur med Google", "description": "Sync Clock" }, - "remember_phrase": { - "message": "Husk adgangskoder", - "description": "Remember Passphrase" - }, "clock_too_far_off": { - "message": "Advarsel!!!! Dit lokale ur er for langt væk, du skal rette det, inden du fortsætter.", + "message": "Advarsel! Dit lokale ur er for unøjagtigt. Ret det, før du fortsætter.", "description": "Local Time is Too Far Off" }, "remind_backup": { @@ -186,11 +178,11 @@ "description": "Remind Backup" }, "capture_failed": { - "message": "Optagelsen mislykkedes. Genindlæs siden igen, og prøv igen.", + "message": "Optagelsen mislykkedes. Genindlæs siden, og prøv igen.", "description": "Capture Failed" }, "capture_local_file_failed": { - "message": "Prøver du at scanne QR-koden fra en lokal fil? Brug i stedet Importér QR-billedsikkerhedskopier.", + "message": "Prøver du at scanne QR-koden fra en lokal fil? Brug i stedet Importér QR-billedsikkerhedskopi.", "description": "Import QR image backup instead of scan local image" }, "based_on_time": { @@ -206,7 +198,7 @@ "description": "Popup Page Settings" }, "scale": { - "message": "Skaler", + "message": "Skalering", "description": "Scale" }, "export_info": { @@ -218,11 +210,11 @@ "description": "Download backup file." }, "import_backup": { - "message": "Importer sikkerhedskopi", + "message": "Importér sikkerhedskopi", "description": "Import backup." }, "import_backup_file": { - "message": "Importer sikkerhedskopifil", + "message": "Importér sikkerhedskopifil", "description": "Import backup file." }, "import_backup_qr": { @@ -230,23 +222,55 @@ "description": "Import qr image backup." }, "import_qr_images": { - "message": "Import QR Images", + "message": "Importér QR-billeder", "description": "Import qr images. Shown as add account method." }, "import_backup_code": { - "message": "Importer sikkerhedskopiering af tekst", + "message": "Importér OTPAuth-URI", "description": "Import backup code." }, "import_otp_urls": { - "message": "Import OTP URLs", + "message": "Importér OTPAuth-URI", "description": "Import OTP URLs. Shown as add account method." }, + "import_drop_file": { + "message": "Slip en sikkerhedskopifil her, eller klik for at gennemse", + "description": "File import dropzone title" + }, + "import_file_accepts": { + "message": "Understøtter .json- og .txt-eksporter", + "description": "File import dropzone hint" + }, + "import_choose_file": { + "message": "Vælg fil", + "description": "File import dropzone button" + }, + "import_encrypted_note": { + "message": "Hvis filen er krypteret, bliver du bedt om dens adgangskode, efter du har valgt den.", + "description": "File import note about encrypted backups" + }, + "import_enc_required": { + "message": "Krypteret — adgangskode påkrævet", + "description": "Badge on a picked encrypted backup file" + }, + "import_pass_placeholder": { + "message": "Indtast filens adgangskode", + "description": "Passphrase input placeholder for encrypted import" + }, + "import_pass_hint": { + "message": "Dette er den adgangskode, du angav, da du oprettede sikkerhedskopien.", + "description": "Hint under the import passphrase field" + }, + "import_decrypt": { + "message": "Dekryptér og importér", + "description": "Button to decrypt and import an encrypted backup" + }, "import_backup_qr_partly_failed": { "message": "Import lykkedes, men nogle QR-koder kunne ikke genkendes.", "description": "Import successful, but some QR image cannot be recognized." }, "import_backup_qr_in_batches": { - "message": "Du kan vælge flere filer at importere backup i partier.", + "message": "Du kan vælge flere filer og importere sikkerhedskopier i partier.", "description": "You can select multiple image files to import backup in batches." }, "show_all_entries": { @@ -254,7 +278,7 @@ "description": "Show all entries." }, "dropbox_risk": { - "message": "Advarsel: sikkerhedskopier er ikke krypteret. Brug det på din egen risiko.", + "message": "Advarsel: sikkerhedskopier er ikke krypteret. Brug på eget ansvar.", "description": "Backup risk warning." }, "import_error_password": { @@ -262,7 +286,7 @@ "description": "Error password warning when import backups." }, "local_passphrase_warning": { - "message": "Din adgangskode gemmes lokalt, skal du straks ændre den i sikkerheds-menuen.", + "message": "Din adgangskode gemmes lokalt. Skift den straks i sikkerhedsmenuen.", "description": "localStorage password warning." }, "remove": { @@ -290,21 +314,113 @@ "description": "Edit" }, "manual_dropbox": { - "message": "Manuel Sikkerhedskopi", + "message": "Manuel sikkerhedskopi", "description": "Manual backup" }, "use_autofill": { "message": "Brug autofyld", "description": "Use Autofill" }, - "use_high_contrast": { - "message": "Brug høj kontrast", - "description": "Use High Contrast" - }, "theme": { "message": "Tema", "description": "Theme" }, + "settings_appearance": { + "message": "Udseende", + "description": "Settings section: appearance" + }, + "settings_general": { + "message": "Generelt", + "description": "Settings section: general" + }, + "edit_accounts": { + "message": "Rediger konti", + "description": "Header title while in edit mode" + }, + "show_qr": { + "message": "Vis QR-kode", + "description": "Context-menu action to show an account's QR code" + }, + "pin_to_top": { + "message": "Fastgør øverst", + "description": "Context-menu action to pin an account to the top" + }, + "unpin": { + "message": "Frigør", + "description": "Context-menu action to unpin an account" + }, + "vault_locked": { + "message": "Boksen er låst", + "description": "Title on the unlock screen" + }, + "unlock": { + "message": "Lås op", + "description": "Unlock button" + }, + "qr_transfer_title": { + "message": "Overfør denne konto", + "description": "QR sheet caption title" + }, + "qr_transfer_desc": { + "message": "Skan denne kode med OTPilot på en anden enhed for at overføre denne konto — den forlader aldrig dine enheder.", + "description": "QR sheet caption body" + }, + "backup_on_device": { + "message": "På denne enhed", + "description": "Backup section: local" + }, + "backup_cloud_sync": { + "message": "Cloud-synkronisering", + "description": "Backup section: cloud" + }, + "backup_connected": { + "message": "Forbundet", + "description": "Cloud provider connected status" + }, + "backup_not_connected": { + "message": "Ikke forbundet", + "description": "Cloud provider not-connected status" + }, + "showing_codes_for": { + "message": "Viser koder for", + "description": "Smart-filter banner prefix, followed by the site domain" + }, + "other_accounts": { + "message": "Andre konti", + "description": "Divider above non-matching accounts in the filter view" + }, + "filter_to_site": { + "message": "Filtrer til", + "description": "Banner prefix to re-apply the site filter, followed by the domain" + }, + "onboarding_title": { + "message": "Koder, der følger med dig", + "description": "Onboarding headline" + }, + "onboarding_subtitle": { + "message": "Generér sikre to-faktor-koder til alle dine konti — krypteret, offline og altid kun ét tryk fra at blive kopieret.", + "description": "Onboarding subtitle" + }, + "onboarding_feature_offline": { + "message": "Fungerer helt offline — intet forlader din enhed", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_encrypted": { + "message": "Krypteret boks med valgfri adgangskodelås", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_scan": { + "message": "Skan en QR-kode, eller importér en eksisterende sikkerhedskopi", + "description": "Onboarding feature bullet" + }, + "onboarding_get_started": { + "message": "Kom i gang", + "description": "Onboarding primary button" + }, + "onboarding_import": { + "message": "Jeg har en sikkerhedskopi, der skal importeres", + "description": "Onboarding secondary button" + }, "theme_light": { "message": "Lys", "description": "Light theme" @@ -313,28 +429,20 @@ "message": "Mørk", "description": "Dark theme" }, - "theme_simple": { - "message": "Simpel", - "description": "Simple theme" + "theme_auto": { + "message": "Automatisk", + "description": "Follow the system light/dark setting" }, "theme_compact": { "message": "Kompakt", "description": "Compact theme" }, "theme_high_contrast": { - "message": "Høj Kontrast", + "message": "Høj kontrast", "description": "High Contrast theme" }, - "theme_flat": { - "message": "Flad", - "description": "Flat theme" - }, - "storage_sync_info": { - "message": "Sikkerhedskopier automatisk dine data til tredjepartslagertjenester.", - "description": "3rd party backup info" - }, "browser_sync": { - "message": "Browser Synkronisering", + "message": "Browsersynkronisering", "description": "Storage location" }, "sign_in": { @@ -342,7 +450,7 @@ "description": "Sign in to 3rd party storage services" }, "sign_in_business": { - "message": "Log ind (Buisness)", + "message": "Log ind (forretning)", "description": "Sign in to 3rd party storage services" }, "onedrive_business_perms": { @@ -353,7 +461,7 @@ "description": "Sign out of 3rd party storage services" }, "token_revoked": { - "message": "Der opstod et problem med forbindelse til din $service$ konto, skal du logge ind igen.", + "message": "Der opstod et problem med forbindelsen til din $SERVICE$-konto. Log ind igen.", "description": "Error authenticating to backup service. $SERVICE$ will be replaced with a proper noun (E.g.: 'Google Drive' or 'Dropbox')", "placeholders": { "service": { @@ -362,16 +470,12 @@ } } }, - "otp_unsupported_warn": { - "message": "Du har en eller flere Steam- eller Blizzard-konti. Ikke-krypterede sikkerhedskopier bruger ikke et standardiseret backupformat.", - "description": "Warning if using account that is not supported by standard backup format." - }, "otp_backup_inform": { "message": "Du kan importere sikkerhedskopier fra nogle andre applikationer.", "description": "Info text on import page" }, "otp_backup_learn": { - "message": "Lær mere", + "message": "Få mere at vide", "description": "learn more link on import page. Placed after otp_backup_inform" }, "loading": { @@ -393,7 +497,7 @@ "message": "Type" }, "invalid": { - "message": "Ugyldigt" + "message": "Ugyldig" }, "digits": { "message": "Cifre" @@ -402,14 +506,11 @@ "message": "Algoritme" }, "smart_filter": { - "message": "Smart Filter" + "message": "Smart filter" }, "backup": { "message": "Sikkerhedskopi" }, - "backup_file_info": { - "message": "Sikkerhedskopier dine data til en fil." - }, "password_policy_default_hint": { "message": "Din adgangskode opfylder ikke din organisations sikkerhedskrav. Kontakt din administrator for mere information." }, @@ -423,10 +524,10 @@ "message": "Aktivér auto-lås for at beskytte dine data." }, "advisor_insight_browser_sync_not_enabled": { - "message": "Browsersynkronisering er deaktiveret. Aktivering af det gør det muligt at synkronisere konti på tværs af browsere." + "message": "Browsersynkronisering er deaktiveret. Aktivering gør det muligt at synkronisere konti på tværs af browsere." }, "advisor_insight_auto_fill_not_enabled": { - "message": "Autoudfyld kan aktiveres for automatisk at udfylde koder på hjemmesider." + "message": "Autofyld kan aktiveres for automatisk at udfylde koder på websteder." }, "advisor_insight_smart_filter_not_enabled": { "message": "Aktivering af smart filter giver hurtig adgang til konti." @@ -462,10 +563,10 @@ "message": "Tilladelser" }, "permission_revoke": { - "message": "Træk tilbage" + "message": "Tilbagekald" }, "permission_show_required_permissions": { - "message": "Vis ikke-genkaldelige tilladelser" + "message": "Vis ikke-tilbagekaldelige tilladelser" }, "permission_required": { "message": "Dette er en krævet tilladelse og kan ikke tilbagekaldes." @@ -477,48 +578,40 @@ "message": "Adgang til browserlagring for at gemme kontodata." }, "permission_identity": { - "message": "Tillader at logge ind på 3. parts lagertjenester." + "message": "Tillader login til tredjeparts lagringstjenester." }, "permission_alarms": { - "message": "Allows auto-lock to work." + "message": "Tillader, at auto-lås fungerer." }, "permission_scripting": { - "message": "Inject scripts into he current tab to scan QR codes and allow auto-fill to work." + "message": "Indsætter scripts i den aktuelle fane for at skanne QR-koder og gøre det muligt for autofyld at fungere." }, "permission_clipboard_write": { - "message": "Giver skrivebeskyttet adgang til udklipsholderen for at kopiere koder til udklipsholderen, når du klikker på kontoen." + "message": "Giver adgang med kun skriverettighed til udklipsholderen, så koder kan kopieres til udklipsholderen, når du klikker på kontoen." }, "permission_context_menus": { - "message": "Tilføjer Authenticator til kontekstmenuen." + "message": "Tilføjer OTPilot til kontekstmenuen." }, "permission_sync_clock": { "message": "Tillader ur-synkronisering med Google." }, "permission_dropbox": { - "message": "Tillader backup til Dropbox." + "message": "Tillader sikkerhedskopiering til Dropbox." }, "permission_dropbox_cannot_revoke": { - "message": "Du skal deaktivere Dropbox backup først." - }, - "permission_drive": { - "message": "Tillader backup til Google Drev." - }, - "permission_drive_cannot_revoke": { - "message": "Du skal deaktivere Google Drev backup først." - }, - "permission_onedrive": { - "message": "Tillader backup til OneDrive." - }, - "permission_onedrive_cannot_revoke": { - "message": "Du skal deaktivere OneDrive backup først." + "message": "Du skal deaktivere Dropbox-sikkerhedskopiering først." }, "permission_unknown_permission": { "message": "Ukendt tilladelse. Hvis du ser denne besked, så send venligst en fejlrapport." }, "phrase_wrong": { - "message": "Password incorrect" + "message": "Forkert adgangskode" }, "activate_auto_filter": { - "message": "Warning: Smart filter loosely matches the domain name to an account. Always verify that you are on the correct website before entering a code!" + "message": "Advarsel: Smart filter matcher domænenavnet løst til en konto. Kontrollér altid, at du er på det korrekte websted, før du indtaster en kode!" + }, + "backup_unavailable": { + "message": "Endnu ikke tilgængelig", + "description": "Shown on a cloud provider that is not implemented yet." } } diff --git a/_locales/de/messages.json b/_locales/de/messages.json index 49f1197d3..9332b22d6 100644 --- a/_locales/de/messages.json +++ b/_locales/de/messages.json @@ -1,14 +1,22 @@ { "extName": { - "message": "Authentifizierung", + "message": "OTPilot Authenticator", "description": "Extension Name." }, "extShortName": { - "message": "Authenticator", + "message": "OTPilot", "description": "Extension Short Name." }, + "host": { + "message": "Website", + "description": "Bound website host used to restrict autofill to a matching page." + }, + "backup_requires_password": { + "message": "Legen Sie zuerst ein Passwort fest, um die Cloud-Sicherung zu nutzen, damit Ihre Geheimnisse verschlüsselt werden, bevor sie dieses Gerät verlassen.", + "description": "Shown in the cloud backup section when no master password is set." + }, "extDesc": { - "message": "Authenticator erzeugt Zwei-Faktor-Authentifizierungscodes in Ihrem Browser.", + "message": "OTPilot Authenticator generiert Zwei-Faktor-Codes (2FA) mit verschlüsselter Cloud-Sicherung, hostgebundenem Autofill und Sicherheitsempfehlungen.", "description": "Extension Description." }, "added": { @@ -23,12 +31,16 @@ "message": "Ungültiges Kontogeheimnis", "description": "Secret Error." }, + "errorissuer": { + "message": "Herausgeber darf \"::\" nicht enthalten", + "description": "Issuer Error." + }, "add_code": { "message": "Konto hinzufügen", "description": "Add account." }, "add_qr": { - "message": "QR Code scannen", + "message": "QR-Code scannen", "description": "Scan QR Code." }, "add_secret": { @@ -43,10 +55,6 @@ "message": "Einige Kontodaten wurden nicht erfolgreich importiert.", "description": "Some migration data is broken." }, - "close": { - "message": "Schließen", - "description": "Close." - }, "ok": { "message": "Ok", "description": "OK." @@ -60,7 +68,7 @@ "description": "No." }, "account": { - "message": "Benutzerkonto", + "message": "Konto", "description": "Account." }, "accountName": { @@ -72,7 +80,7 @@ "description": "Issuer." }, "secret": { - "message": "Schlüssel", + "message": "Geheimnis", "description": "Secret." }, "updateSuccess": { @@ -99,10 +107,6 @@ "message": "Aktuelles Passwort", "description": "Current Passphrase." }, - "new_phrase": { - "message": "Neues Passwort", - "description": "New Passphrase." - }, "phrase": { "message": "Passwort", "description": "Passphrase." @@ -112,7 +116,7 @@ "description": "Confirm Passphrase." }, "confirm_delete": { - "message": "Sind Sie sicher, dass Sie diesen Account löschen wollen? Diese Aktion kann nicht rückgängig gemacht werden.", + "message": "Sind Sie sicher, dass Sie dieses Konto löschen wollen? Diese Aktion kann nicht rückgängig gemacht werden.", "description": "Remove entry confirmation" }, "confirm_delete_all": { @@ -120,19 +124,15 @@ "description": "Message that user is required to acknowledge before clearing all data." }, "delete_all": { - "message": "Authenticator zurücksetzen" + "message": "OTPilot zurücksetzen" }, "delete_all_warning": { - "message": "Dies wird alle Ihre Daten löschen und den Authenticator komplett zurücksetzen. Sie werden keine gelöschten Daten wiederherstellen können! Sie sollten erwägen, ein Backup zu speichern, bevor Sie den Authenticator zurücksetzen." + "message": "Dies wird alle Ihre Daten löschen und OTPilot komplett zurücksetzen. Sie werden keine gelöschten Daten wiederherstellen können! Sie sollten erwägen, ein Backup zu speichern, bevor Sie OTPilot zurücksetzen." }, "security_warning": { - "message": "Dieses Passwort wird für die Verschlüsselung ihrer Konten/Accounts benutzt werden. Niemand kann ihnen helfen, wenn Sie dieses Passwort vergessen.", + "message": "Dieses Passwort wird verwendet, um Ihre Konten zu verschlüsseln. Niemand kann Ihnen helfen, wenn Sie dieses Passwort vergessen.", "description": "Passphrase Warning." }, - "update": { - "message": "Aktualisieren", - "description": "Update." - }, "phrase_incorrect": { "message": "Sie können kein neues Konto hinzufügen, bis alle Konten entschlüsselt sind. Bitte geben Sie das richtige Passwort ein, bevor Sie fortfahren.", "description": "Passphrase Incorrect." @@ -157,32 +157,24 @@ "message": "Rückmeldung", "description": "Feedback." }, - "translate": { - "message": "Übersetzen", - "description": "Translate." - }, "source": { "message": "Quellcode", "description": "Source Code." }, "passphrase_info": { - "message": "Kennwort eingeben um Kontoinformationen zu entschlüsseln.", + "message": "Passwort eingeben, um Kontodaten zu entschlüsseln.", "description": "Passphrase Info" }, "sync_clock": { - "message": "Zeitgeber mit Google synchronisieren", + "message": "Uhrzeit mit Google synchronisieren", "description": "Sync Clock" }, - "remember_phrase": { - "message": "Passwort merken", - "description": "Remember Passphrase" - }, "clock_too_far_off": { - "message": "Achtung! Ihre lokale Uhrzeit hat eine zu große Abweichung, bitte beheben bevor Sie fortfahren.", + "message": "Achtung! Ihre lokale Uhrzeit weicht zu stark ab. Bitte beheben Sie dies, bevor Sie fortfahren.", "description": "Local Time is Too Far Off" }, "remind_backup": { - "message": "Haben Sie ein Backup für ihre Accounts? Warten Sie nicht bis es zu spät ist!", + "message": "Haben Sie ein Backup für Ihre Konten? Warten Sie nicht, bis es zu spät ist!", "description": "Remind Backup" }, "capture_failed": { @@ -190,7 +182,7 @@ "description": "Capture Failed" }, "capture_local_file_failed": { - "message": "Wollen Sie QR-Codes von einer lokalen Datei scannen? Verwenden Sie stattdessen QR-Code-Sicherung.", + "message": "Möchten Sie einen QR-Code aus einer lokalen Datei scannen? Verwenden Sie stattdessen „QR-Bildsicherung importieren“.", "description": "Import QR image backup instead of scan local image" }, "based_on_time": { @@ -206,11 +198,11 @@ "description": "Popup Page Settings" }, "scale": { - "message": "Skalieren", + "message": "Skalierung", "description": "Scale" }, "export_info": { - "message": "Warnung: Alle Sicherungen sind unverschlüsselt! Wollen Sie ein Konto zu einer anderen App hinzufügen? Fahren Sie mit dem Mauszeiger über den oberen, rechten Teil des Kontos und drücken Sie die versteckte Schaltfläche.", + "message": "Warnung: Alle Sicherungen sind unverschlüsselt! Wollen Sie ein Konto zu einer anderen App hinzufügen? Fahren Sie mit dem Mauszeiger über den oberen rechten Teil des Kontos und drücken Sie die versteckte Schaltfläche.", "description": "Export menu info text" }, "download_backup": { @@ -234,13 +226,45 @@ "description": "Import qr images. Shown as add account method." }, "import_backup_code": { - "message": "Text-Sicherung importieren", + "message": "OTPAuth-URI importieren", "description": "Import backup code." }, "import_otp_urls": { "message": "OTP-URLs importieren", "description": "Import OTP URLs. Shown as add account method." }, + "import_drop_file": { + "message": "Backup-Datei hierher ziehen oder klicken zum Durchsuchen", + "description": "File import dropzone title" + }, + "import_file_accepts": { + "message": "Unterstützt .json- und .txt-Exporte", + "description": "File import dropzone hint" + }, + "import_choose_file": { + "message": "Datei auswählen", + "description": "File import dropzone button" + }, + "import_encrypted_note": { + "message": "Wenn die Datei verschlüsselt ist, werden Sie nach der Auswahl nach dem Passwort gefragt.", + "description": "File import note about encrypted backups" + }, + "import_enc_required": { + "message": "Verschlüsselt – Passwort erforderlich", + "description": "Badge on a picked encrypted backup file" + }, + "import_pass_placeholder": { + "message": "Passwort der Datei eingeben", + "description": "Passphrase input placeholder for encrypted import" + }, + "import_pass_hint": { + "message": "Das ist das Passwort, das Sie beim Erstellen des Backups festgelegt haben.", + "description": "Hint under the import passphrase field" + }, + "import_decrypt": { + "message": "Entschlüsseln & importieren", + "description": "Button to decrypt and import an encrypted backup" + }, "import_backup_qr_partly_failed": { "message": "Import erfolgreich, aber einige QR-Codes konnten nicht erkannt werden.", "description": "Import successful, but some QR image cannot be recognized." @@ -254,7 +278,7 @@ "description": "Show all entries." }, "dropbox_risk": { - "message": "Vorsicht: Sicherungen, die in Dropbox gespeichert werden, sind nicht verschlüsselt. Benutzung auf eigene Gefahr.", + "message": "Vorsicht: Sicherungen sind unverschlüsselt. Nutzung auf eigene Gefahr.", "description": "Backup risk warning." }, "import_error_password": { @@ -262,7 +286,7 @@ "description": "Error password warning when import backups." }, "local_passphrase_warning": { - "message": "Ihr Passwort wird lokal gespeichert, bitte ändern Sie es im Sicherheitsmenü sofort.", + "message": "Ihr Passwort wird lokal gespeichert. Bitte ändern Sie es sofort im Sicherheitsmenü.", "description": "localStorage password warning." }, "remove": { @@ -297,14 +321,106 @@ "message": "Autofill verwenden", "description": "Use Autofill" }, - "use_high_contrast": { - "message": "Hohen Kontrast benutzen", - "description": "Use High Contrast" - }, "theme": { "message": "Farbschema", "description": "Theme" }, + "settings_appearance": { + "message": "Erscheinungsbild", + "description": "Settings section: appearance" + }, + "settings_general": { + "message": "Allgemein", + "description": "Settings section: general" + }, + "edit_accounts": { + "message": "Konten bearbeiten", + "description": "Header title while in edit mode" + }, + "show_qr": { + "message": "QR-Code anzeigen", + "description": "Context-menu action to show an account's QR code" + }, + "pin_to_top": { + "message": "Oben anheften", + "description": "Context-menu action to pin an account to the top" + }, + "unpin": { + "message": "Lösen", + "description": "Context-menu action to unpin an account" + }, + "vault_locked": { + "message": "Tresor gesperrt", + "description": "Title on the unlock screen" + }, + "unlock": { + "message": "Entsperren", + "description": "Unlock button" + }, + "qr_transfer_title": { + "message": "Dieses Konto übertragen", + "description": "QR sheet caption title" + }, + "qr_transfer_desc": { + "message": "Scannen Sie diesen Code mit OTPilot auf einem anderen Gerät, um dieses Konto zu übertragen – es verlässt niemals Ihre Geräte.", + "description": "QR sheet caption body" + }, + "backup_on_device": { + "message": "Auf diesem Gerät", + "description": "Backup section: local" + }, + "backup_cloud_sync": { + "message": "Cloud-Synchronisierung", + "description": "Backup section: cloud" + }, + "backup_connected": { + "message": "Verbunden", + "description": "Cloud provider connected status" + }, + "backup_not_connected": { + "message": "Nicht verbunden", + "description": "Cloud provider not-connected status" + }, + "showing_codes_for": { + "message": "Codes werden angezeigt für", + "description": "Smart-filter banner prefix, followed by the site domain" + }, + "other_accounts": { + "message": "Weitere Konten", + "description": "Divider above non-matching accounts in the filter view" + }, + "filter_to_site": { + "message": "Filtern nach", + "description": "Banner prefix to re-apply the site filter, followed by the domain" + }, + "onboarding_title": { + "message": "Codes, die Sie immer dabei haben", + "description": "Onboarding headline" + }, + "onboarding_subtitle": { + "message": "Erstellen Sie sichere Zwei-Faktor-Codes für jedes Konto – verschlüsselt, offline und mit nur einem Tipp kopierbereit.", + "description": "Onboarding subtitle" + }, + "onboarding_feature_offline": { + "message": "Funktioniert komplett offline – nichts verlässt Ihr Gerät", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_encrypted": { + "message": "Verschlüsselter Tresor mit optionaler Passwortsperre", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_scan": { + "message": "QR-Code scannen oder vorhandenes Backup importieren", + "description": "Onboarding feature bullet" + }, + "onboarding_get_started": { + "message": "Los geht's", + "description": "Onboarding primary button" + }, + "onboarding_import": { + "message": "Ich habe ein Backup zum Importieren", + "description": "Onboarding secondary button" + }, "theme_light": { "message": "Hell", "description": "Light theme" @@ -313,9 +429,9 @@ "message": "Dunkel", "description": "Dark theme" }, - "theme_simple": { - "message": "Einfach", - "description": "Simple theme" + "theme_auto": { + "message": "Automatisch", + "description": "Follow the system light/dark setting" }, "theme_compact": { "message": "Kompakt", @@ -325,14 +441,6 @@ "message": "Hoher Kontrast", "description": "High Contrast theme" }, - "theme_flat": { - "message": "Flach", - "description": "Flat theme" - }, - "storage_sync_info": { - "message": "Automatische Sicherung auf Drittanbieter-Speicher.", - "description": "3rd party backup info" - }, "browser_sync": { "message": "Browser-Synchronisation", "description": "Storage location" @@ -353,7 +461,7 @@ "description": "Sign out of 3rd party storage services" }, "token_revoked": { - "message": "Es gab ein Problem beim Verbinden zu ihrem $SERVICE$ Konto, bitte melden sie sich neu an.", + "message": "Es gab ein Problem beim Verbinden mit Ihrem $SERVICE$-Konto. Bitte melden Sie sich erneut an.", "description": "Error authenticating to backup service. $SERVICE$ will be replaced with a proper noun (E.g.: 'Google Drive' or 'Dropbox')", "placeholders": { "service": { @@ -362,10 +470,6 @@ } } }, - "otp_unsupported_warn": { - "message": "Du hast ein oder mehrere Steam- oder Blizzard-Konten. Unverschlüsselte Backups werden kein standardisiertes Backup-Format verwenden.", - "description": "Warning if using account that is not supported by standard backup format." - }, "otp_backup_inform": { "message": "Sie können Backups aus einigen anderen Anwendungen importieren.", "description": "Info text on import page" @@ -384,10 +488,10 @@ "message": "Minuten" }, "advanced": { - "message": "Erweiterte" + "message": "Erweitert" }, "period": { - "message": "Zeitraum" + "message": "Intervall" }, "type": { "message": "Typ" @@ -402,14 +506,11 @@ "message": "Algorithmus" }, "smart_filter": { - "message": "Intelligente Filter" + "message": "Intelligenter Filter" }, "backup": { "message": "Sicherung" }, - "backup_file_info": { - "message": "Sichern Sie Ihre Daten in eine Datei." - }, "password_policy_default_hint": { "message": "Ihr Passwort entspricht nicht den Sicherheitsanforderungen Ihrer Organisation. Kontaktieren Sie Ihren Administrator für weitere Informationen." }, @@ -417,28 +518,28 @@ "message": "Ratgeber" }, "advisor_insight_password_not_set": { - "message": "Lege ein Passwort zum Schutz deiner Daten fest." + "message": "Legen Sie ein Passwort fest, um Ihre Daten zu schützen." }, "advisor_insight_auto_lock_not_set": { - "message": "Erlaube automatisches Sperren, um deine Daten zu schützen." + "message": "Aktivieren Sie die automatische Sperre, um Ihre Daten zu schützen." }, "advisor_insight_browser_sync_not_enabled": { - "message": "Browser-Synchronisation ist deaktiviert. Aktivieren erlaubt die Synchronisierung von Konten in Browsern." + "message": "Browser-Synchronisation ist deaktiviert. Das Aktivieren ermöglicht die browserübergreifende Synchronisierung von Konten." }, "advisor_insight_auto_fill_not_enabled": { "message": "Autofill kann aktiviert werden, um automatisch Codes in Webseiten auszufüllen." }, "advisor_insight_smart_filter_not_enabled": { - "message": "Das Aktivieren von Smart Filter erlaubt schnellen Zugriff auf Konten." + "message": "Das Aktivieren des intelligenten Filters erlaubt schnellen Zugriff auf Konten." }, "show_all_insights": { - "message": "Alle Warnungen anzeigen." + "message": "Alle Hinweise anzeigen." }, "no_insight_available": { - "message": "Keine Warnungen, alle Einstellungen entsprechen den Empfehlungen!" + "message": "Keine Hinweise vorhanden, alles sieht gut aus!" }, "danger": { - "message": "Risiko" + "message": "Gefahr" }, "warning": { "message": "Warnung" @@ -468,7 +569,7 @@ "message": "Nicht widerrufbare Berechtigungen anzeigen" }, "permission_required": { - "message": "Dies ist eine erforderliche Erlaubnis und kann nicht widerrufen werden." + "message": "Dies ist eine erforderliche Berechtigung und kann nicht widerrufen werden." }, "permission_active_tab": { "message": "Zugriff auf die aktuelle Registerkarte, um QR-Codes zu scannen." @@ -477,19 +578,19 @@ "message": "Zugriff auf Browserspeicherung, um Kontodaten zu speichern." }, "permission_identity": { - "message": "Einloggen bei externen Speicherdiensten zulassen." + "message": "Anmeldung bei externen Speicherdiensten zulassen." }, "permission_alarms": { - "message": "Allows auto-lock to work." + "message": "Ermöglicht die automatische Sperre." }, "permission_scripting": { - "message": "Inject scripts into he current tab to scan QR codes and allow auto-fill to work." + "message": "Fügt Skripte in die aktuelle Registerkarte ein, um QR-Codes zu scannen und Autofill zu ermöglichen." }, "permission_clipboard_write": { "message": "Gewährt Schreibzugriff auf die Zwischenablage, um Codes in die Zwischenablage zu kopieren, wenn Sie auf das Konto klicken." }, "permission_context_menus": { - "message": "Fügt Authenticator dem Kontextmenü hinzu." + "message": "Fügt OTPilot dem Kontextmenü hinzu." }, "permission_sync_clock": { "message": "Synchronisierung mit Google zulassen." @@ -500,25 +601,17 @@ "permission_dropbox_cannot_revoke": { "message": "Sie müssen zuerst das Dropbox-Backup deaktivieren." }, - "permission_drive": { - "message": "Backup auf OneDrive zulassen." - }, - "permission_drive_cannot_revoke": { - "message": "Sie müssen zuerst das Google-Backup deaktivieren." - }, - "permission_onedrive": { - "message": "Backup auf OneDrive zulassen." - }, - "permission_onedrive_cannot_revoke": { - "message": "Sie müssen zuerst das OneDrive-Backup deaktivieren." - }, "permission_unknown_permission": { "message": "Unbekannte Berechtigung. Wenn Sie diese Nachricht sehen, senden Sie bitte einen Fehlerbericht." }, "phrase_wrong": { - "message": "Password incorrect" + "message": "Falsches Passwort" }, "activate_auto_filter": { - "message": "Warning: Smart filter loosely matches the domain name to an account. Always verify that you are on the correct website before entering a code!" + "message": "Warnung: Der intelligente Filter ordnet den Domainnamen nur ungefähr einem Konto zu. Überprüfen Sie immer, ob Sie sich auf der richtigen Website befinden, bevor Sie einen Code eingeben!" + }, + "backup_unavailable": { + "message": "Noch nicht verfügbar", + "description": "Shown on a cloud provider that is not implemented yet." } } diff --git a/_locales/el/messages.json b/_locales/el/messages.json index 8aead6ffc..6b9699995 100644 --- a/_locales/el/messages.json +++ b/_locales/el/messages.json @@ -1,14 +1,22 @@ { "extName": { - "message": "Επαληθευτής", + "message": "OTPilot Authenticator", "description": "Extension Name." }, "extShortName": { - "message": "Επαληθευτής", + "message": "OTPilot", "description": "Extension Short Name." }, + "host": { + "message": "Ιστότοπος", + "description": "Bound website host used to restrict autofill to a matching page." + }, + "backup_requires_password": { + "message": "Ορίστε πρώτα έναν κωδικό πρόσβασης για να χρησιμοποιήσετε το αντίγραφο ασφαλείας στο cloud, ώστε τα μυστικά σας να κρυπτογραφούνται πριν φύγουν από αυτή τη συσκευή.", + "description": "Shown in the cloud backup section when no master password is set." + }, "extDesc": { - "message": "Ο επαληθευτής δημιουργεί κωδικούς ταυτότητας δυο παραγόντων στο πρόγραμμα περιήγησής σας.", + "message": "Το OTPilot Authenticator δημιουργεί κωδικούς δύο παραγόντων (2FA), με κρυπτογραφημένο αντίγραφο ασφαλείας στο cloud, αυτόματη συμπλήρωση βάσει ιστότοπου και συμβουλές ασφαλείας.", "description": "Extension Description." }, "added": { @@ -20,9 +28,13 @@ "description": "QR Error." }, "errorsecret": { - "message": "Μην έγκυρο μυστικό λογαριασμού", + "message": "Μη έγκυρο μυστικό λογαριασμού", "description": "Secret Error." }, + "errorissuer": { + "message": "Ο εκδότης δεν μπορεί να περιέχει «::»", + "description": "Issuer Error." + }, "add_code": { "message": "Προσθήκη λογαριασμού", "description": "Add account." @@ -43,10 +55,6 @@ "message": "Ορισμένα από τα δεδομένα του λογαριασμού σας δεν εισήχθησαν με επιτυχία.", "description": "Some migration data is broken." }, - "close": { - "message": "Κλείσιμο", - "description": "Close." - }, "ok": { "message": "Εντάξει", "description": "OK." @@ -96,13 +104,9 @@ "description": "Security." }, "current_phrase": { - "message": "Τρέχον Κωδικός", + "message": "Τρέχων κωδικός πρόσβασης", "description": "Current Passphrase." }, - "new_phrase": { - "message": "Νέος κωδικός πρόσβασης", - "description": "New Passphrase." - }, "phrase": { "message": "Κωδικός πρόσβασης", "description": "Passphrase." @@ -129,10 +133,6 @@ "message": "Αυτός ο κωδικός πρόσβασης θα χρησιμοποιηθεί για την κρυπτογράφηση των λογαριασμών σας. Κανείς δεν μπορεί να σας βοηθήσει σε περίπτωση που τον ξεχάσετε.", "description": "Passphrase Warning." }, - "update": { - "message": "Ενημέρωση", - "description": "Update." - }, "phrase_incorrect": { "message": "Δεν μπορείτε να προσθέσετε ένα νέο λογαριασμό μέχρι όλοι οι λογαριασμοί να έχουν αποκρυπτογραφηθεί. Εισάγετε τον σωστό κωδικό πρόσβασης για να συνεχίσετε.", "description": "Passphrase Incorrect." @@ -154,13 +154,9 @@ "description": "Copied." }, "feedback": { - "message": "Αξιολογήστε μας", + "message": "Σχόλια", "description": "Feedback." }, - "translate": { - "message": "Συνεισφέρετε στη μετάφραση", - "description": "Translate." - }, "source": { "message": "Πηγαίος κώδικας", "description": "Source Code." @@ -173,12 +169,8 @@ "message": "Συγχρονισμός ρολογιού με την Google", "description": "Sync Clock" }, - "remember_phrase": { - "message": "Απομνημόνευση κωδικού πρόσβασης", - "description": "Remember Passphrase" - }, "clock_too_far_off": { - "message": "Προσοχή! Τοπικό ρολόι σας απέχει πάρα πολύ από την πραγματική ώρα. Διορθώστε το για να συνεχίσετε.", + "message": "Προσοχή! Το τοπικό ρολόι σας απέχει πάρα πολύ από την πραγματική ώρα. Διορθώστε το για να συνεχίσετε.", "description": "Local Time is Too Far Off" }, "remind_backup": { @@ -214,7 +206,7 @@ "description": "Export menu info text" }, "download_backup": { - "message": "Λήψη αρχείου", + "message": "Λήψη αρχείου αντιγράφου ασφαλείας", "description": "Download backup file." }, "import_backup": { @@ -222,7 +214,7 @@ "description": "Import backup." }, "import_backup_file": { - "message": "Import Backup File", + "message": "Εισαγωγή αρχείου αντιγράφου ασφαλείας", "description": "Import backup file." }, "import_backup_qr": { @@ -234,19 +226,51 @@ "description": "Import qr images. Shown as add account method." }, "import_backup_code": { - "message": "Εισαγωγή αντιγράφου ασφαλείας κειμένου", + "message": "Εισαγωγή OTPAuth URI", "description": "Import backup code." }, "import_otp_urls": { - "message": "Εισαγωγή διευθύνσεων URL OTP", + "message": "Εισαγωγή OTPAuth URI", "description": "Import OTP URLs. Shown as add account method." }, + "import_drop_file": { + "message": "Σύρετε ένα αρχείο αντιγράφου ασφαλείας ή κάντε κλικ για περιήγηση", + "description": "File import dropzone title" + }, + "import_file_accepts": { + "message": "Δέχεται εξαγωγές .json και .txt", + "description": "File import dropzone hint" + }, + "import_choose_file": { + "message": "Επιλογή αρχείου", + "description": "File import dropzone button" + }, + "import_encrypted_note": { + "message": "Αν το αρχείο είναι κρυπτογραφημένο, θα σας ζητηθεί ο κωδικός πρόσβασής του μετά την επιλογή.", + "description": "File import note about encrypted backups" + }, + "import_enc_required": { + "message": "Κρυπτογραφημένο — απαιτείται κωδικός πρόσβασης", + "description": "Badge on a picked encrypted backup file" + }, + "import_pass_placeholder": { + "message": "Εισαγάγετε τον κωδικό πρόσβασης του αρχείου", + "description": "Passphrase input placeholder for encrypted import" + }, + "import_pass_hint": { + "message": "Αυτός είναι ο κωδικός πρόσβασης που ορίσατε όταν δημιουργήσατε το αντίγραφο ασφαλείας.", + "description": "Hint under the import passphrase field" + }, + "import_decrypt": { + "message": "Αποκρυπτογράφηση και εισαγωγή", + "description": "Button to decrypt and import an encrypted backup" + }, "import_backup_qr_partly_failed": { "message": "Η εισαγωγή ολοκληρώθηκε με επιτυχία, αλλά ορισμένοι κώδικες QR δεν αναγνωρίστηκαν.", "description": "Import successful, but some QR image cannot be recognized." }, "import_backup_qr_in_batches": { - "message": "Μπορείτε να επιλέξετε περισσότερα από ένα αρχεία αντιγράφων ασφαλείας.", + "message": "Μπορείτε να επιλέξετε πολλά αρχεία για να εισαγάγετε αντίγραφα ασφαλείας μαζικά.", "description": "You can select multiple image files to import backup in batches." }, "show_all_entries": { @@ -297,14 +321,106 @@ "message": "Αυτόματη συμπλήρωση", "description": "Use Autofill" }, - "use_high_contrast": { - "message": "Χρήση υψηλής αντίθεσης", - "description": "Use High Contrast" - }, "theme": { "message": "Θέμα", "description": "Theme" }, + "settings_appearance": { + "message": "Εμφάνιση", + "description": "Settings section: appearance" + }, + "settings_general": { + "message": "Γενικά", + "description": "Settings section: general" + }, + "edit_accounts": { + "message": "Επεξεργασία λογαριασμών", + "description": "Header title while in edit mode" + }, + "show_qr": { + "message": "Εμφάνιση κωδικού QR", + "description": "Context-menu action to show an account's QR code" + }, + "pin_to_top": { + "message": "Καρφίτσωμα στην κορυφή", + "description": "Context-menu action to pin an account to the top" + }, + "unpin": { + "message": "Ξεκαρφίτσωμα", + "description": "Context-menu action to unpin an account" + }, + "vault_locked": { + "message": "Το θησαυροφυλάκιο είναι κλειδωμένο", + "description": "Title on the unlock screen" + }, + "unlock": { + "message": "Ξεκλείδωμα", + "description": "Unlock button" + }, + "qr_transfer_title": { + "message": "Μεταφορά αυτού του λογαριασμού", + "description": "QR sheet caption title" + }, + "qr_transfer_desc": { + "message": "Σαρώστε αυτόν τον κωδικό με το OTPilot σε άλλη συσκευή για να μεταφέρετε αυτόν τον λογαριασμό — δεν φεύγει ποτέ από τις συσκευές σας.", + "description": "QR sheet caption body" + }, + "backup_on_device": { + "message": "Σε αυτή τη συσκευή", + "description": "Backup section: local" + }, + "backup_cloud_sync": { + "message": "Συγχρονισμός cloud", + "description": "Backup section: cloud" + }, + "backup_connected": { + "message": "Συνδεδεμένο", + "description": "Cloud provider connected status" + }, + "backup_not_connected": { + "message": "Μη συνδεδεμένο", + "description": "Cloud provider not-connected status" + }, + "showing_codes_for": { + "message": "Εμφάνιση κωδικών για", + "description": "Smart-filter banner prefix, followed by the site domain" + }, + "other_accounts": { + "message": "Άλλοι λογαριασμοί", + "description": "Divider above non-matching accounts in the filter view" + }, + "filter_to_site": { + "message": "Φιλτράρισμα σε", + "description": "Banner prefix to re-apply the site filter, followed by the domain" + }, + "onboarding_title": { + "message": "Κωδικοί που σας συνοδεύουν παντού", + "description": "Onboarding headline" + }, + "onboarding_subtitle": { + "message": "Δημιουργήστε ασφαλείς κωδικούς δύο παραγόντων για κάθε λογαριασμό — κρυπτογραφημένους, εκτός σύνδεσης και πάντα ένα πάτημα μακριά από την αντιγραφή.", + "description": "Onboarding subtitle" + }, + "onboarding_feature_offline": { + "message": "Λειτουργεί πλήρως εκτός σύνδεσης — τίποτα δεν φεύγει από τη συσκευή σας", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_encrypted": { + "message": "Κρυπτογραφημένο θησαυροφυλάκιο με προαιρετικό κλείδωμα κωδικού πρόσβασης", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_scan": { + "message": "Σαρώστε έναν κωδικό QR ή εισαγάγετε ένα υπάρχον αντίγραφο ασφαλείας", + "description": "Onboarding feature bullet" + }, + "onboarding_get_started": { + "message": "Ξεκινήστε", + "description": "Onboarding primary button" + }, + "onboarding_import": { + "message": "Έχω ένα αντίγραφο ασφαλείας για εισαγωγή", + "description": "Onboarding secondary button" + }, "theme_light": { "message": "Φωτεινό", "description": "Light theme" @@ -313,9 +429,9 @@ "message": "Σκούρο", "description": "Dark theme" }, - "theme_simple": { - "message": "Απλό", - "description": "Simple theme" + "theme_auto": { + "message": "Αυτόματο", + "description": "Follow the system light/dark setting" }, "theme_compact": { "message": "Συμπαγές", @@ -325,16 +441,8 @@ "message": "Υψηλή αντίθεση", "description": "High Contrast theme" }, - "theme_flat": { - "message": "Απλό", - "description": "Flat theme" - }, - "storage_sync_info": { - "message": "Δημιουργήστε αυτόματα αντίγραφα ασφαλείας των δεδομένων σας σε υπηρεσίες αποθήκευσης τρίτων.", - "description": "3rd party backup info" - }, "browser_sync": { - "message": "Συγχρονισμός δεδομένων", + "message": "Συγχρονισμός προγράμματος περιήγησης", "description": "Storage location" }, "sign_in": { @@ -362,10 +470,6 @@ } } }, - "otp_unsupported_warn": { - "message": "Έχετε έναν ή παραπάνω Steam ή Blizzard λογαριασμούς. Τα αντίγραφα ασφαλείας που δεν έχουν κρυπτογραφηθεί δε θα ακολουθήσουν τη συνηθισμένη μορφή των άλλων αντιγράφων.", - "description": "Warning if using account that is not supported by standard backup format." - }, "otp_backup_inform": { "message": "Μπορείτε να εισάγετε αντίγραφα ασφαλείας από άλλες εφαρμογές.", "description": "Info text on import page" @@ -407,11 +511,8 @@ "backup": { "message": "Αντίγραφο Ασφαλείας" }, - "backup_file_info": { - "message": "Αποθηκεύστε τα δεδομένα σας σε ένα αρχείο." - }, "password_policy_default_hint": { - "message": "Ο κωδικός πρόσβασής σας δεν πληροί τις προαπαιτήσεις ασφαλείας του οργανισμού σας. Επικοινωνήστε με τοn διαχειριστή σας για περισσότερες πληροφορίες." + "message": "Ο κωδικός πρόσβασής σας δεν πληροί τις προαπαιτήσεις ασφαλείας του οργανισμού σας. Επικοινωνήστε με τον διαχειριστή σας για περισσότερες πληροφορίες." }, "advisor": { "message": "Σύμβουλος" @@ -465,7 +566,7 @@ "message": "Ανάκληση" }, "permission_show_required_permissions": { - "message": "Εμφάνιση αδειών που δεν ανακαλούνται" + "message": "Εμφάνιση μη ανακλήσιμων δικαιωμάτων" }, "permission_required": { "message": "Αυτό είναι ένα δικαίωμα που απαιτείται και δεν μπορεί να ανακληθεί." @@ -483,10 +584,10 @@ "message": "Επιτρέπει το αυτόματο κλείδωμα να λειτουργήσει." }, "permission_scripting": { - "message": "Χορηγεί κώδικα στην τρέχουσα καρτέλα για να σκανάρει κωδικούς QR και να συμπληρώσει αυτόματα ώστε να λειτουργήσει." + "message": "Εισάγει σενάρια (scripts) στην τρέχουσα καρτέλα για τη σάρωση κωδικών QR και για να λειτουργεί η αυτόματη συμπλήρωση." }, "permission_clipboard_write": { - "message": "Παραχωρεί πρόσβαση στο πρόχειρο για την αντιγραφή και επικόλληση κωδικών όταν κάνετε κλικ στον αντίστοιχο λογαριασμό." + "message": "Παραχωρεί πρόσβαση μόνο για εγγραφή στο πρόχειρο, ώστε να αντιγράφονται οι κωδικοί όταν κάνετε κλικ στον λογαριασμό." }, "permission_context_menus": { "message": "Προσθέτει τον Επαληθευτή στο μενού περιβάλλοντος." @@ -500,18 +601,6 @@ "permission_dropbox_cannot_revoke": { "message": "Πρέπει πρώτα να απενεργοποιήσετε τη δημιουργία αντιγράφων ασφαλείας του Dropbox." }, - "permission_drive": { - "message": "Επιτρέπει τη δημιουργία αντιγράφων ασφαλείας στο Google Drive." - }, - "permission_drive_cannot_revoke": { - "message": "Πρέπει πρώτα να απενεργοποιήσετε τη δημιουργία αντιγράφων ασφαλείας του Google Drive." - }, - "permission_onedrive": { - "message": "Επιτρέπει τη δημιουργία αντιγράφων ασφαλείας στο OneDrive." - }, - "permission_onedrive_cannot_revoke": { - "message": "Πρέπει πρώτα να απενεργοποιήσετε τα αντίγραφα ασφαλείας του OneDrive." - }, "permission_unknown_permission": { "message": "Άγνωστο δικαίωμα. Αν δείτε αυτό το μήνυμα, στείλτε μας μια αναφορά σφάλματος." }, @@ -520,5 +609,9 @@ }, "activate_auto_filter": { "message": "Προσοχή: Το έξυπνο φίλτρο δεν αντιστοιχίζει με ακρίβεια τον κάθε λογαριασμό με την ιστοσελίδα όπου αυτός δημιουργήθηκε. Για τον λόγο αυτό, πρέπει πάντα να ελέγχετε αν βρίσκεστε στη σωστή ιστοσελίδα προτού εισάγετε τον κωδικό!" + }, + "backup_unavailable": { + "message": "Δεν είναι ακόμη διαθέσιμο", + "description": "Shown on a cloud provider that is not implemented yet." } } diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 1509958f1..77aafe6ad 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -1,14 +1,14 @@ { "extName": { - "message": "Authenticator", + "message": "OTPilot Authenticator", "description": "Extension Name." }, "extShortName": { - "message": "Authenticator", + "message": "OTPilot", "description": "Extension Short Name." }, "extDesc": { - "message": "Authenticator generates two-factor authentication codes in your browser.", + "message": "OTPilot Authenticator generates two-factor (2FA) codes, with encrypted cloud backup, host-matched autofill, and security advice.", "description": "Extension Description." }, "added": { @@ -23,6 +23,10 @@ "message": "Invalid account secret", "description": "Secret Error." }, + "errorissuer": { + "message": "Issuer cannot contain \"::\"", + "description": "Issuer Error." + }, "add_code": { "message": "Add account", "description": "Add account." @@ -43,10 +47,6 @@ "message": "Some account data was not imported successfully.", "description": "Some migration data is broken." }, - "close": { - "message": "Close", - "description": "Close." - }, "ok": { "message": "Ok", "description": "OK." @@ -71,6 +71,10 @@ "message": "Issuer", "description": "Issuer." }, + "host": { + "message": "Website", + "description": "Bound website host used to restrict autofill to a matching page." + }, "secret": { "message": "Secret", "description": "Secret." @@ -99,10 +103,6 @@ "message": "Current Password", "description": "Current Passphrase." }, - "new_phrase": { - "message": "New Password", - "description": "New Passphrase." - }, "phrase": { "message": "Password", "description": "Passphrase." @@ -120,19 +120,15 @@ "description": "Message that user is required to acknowledge before clearing all data." }, "delete_all": { - "message": "Reset Authenticator" + "message": "Reset OTPilot" }, "delete_all_warning": { - "message": "This will delete all of your data and completely reset Authenticator. You will not be able to recover any deleted data! You should consider saving a backup before resetting Authenticator." + "message": "This will delete all of your data and completely reset OTPilot. You will not be able to recover any deleted data! You should consider saving a backup before resetting OTPilot." }, "security_warning": { "message": "This password will be used to encrypt your accounts. No one can help you if you forget the password.", "description": "Passphrase Warning." }, - "update": { - "message": "Update", - "description": "Update." - }, "phrase_incorrect": { "message": "You cannot add a new account until all accounts are decrypted. Please enter the correct password before continuing.", "description": "Passphrase Incorrect." @@ -157,10 +153,6 @@ "message": "Feedback", "description": "Feedback." }, - "translate": { - "message": "Translate", - "description": "Translate." - }, "source": { "message": "Source Code", "description": "Source Code." @@ -173,10 +165,6 @@ "message": "Sync Clock with Google", "description": "Sync Clock" }, - "remember_phrase": { - "message": "Remember Password", - "description": "Remember Passphrase" - }, "clock_too_far_off": { "message": "Caution! Your local clock is too far off, please fix it before continuing.", "description": "Local Time is Too Far Off" @@ -234,13 +222,45 @@ "description": "Import qr images. Shown as add account method." }, "import_backup_code": { - "message": "Import Text Backup", + "message": "Import OTPAuth URI", "description": "Import backup code." }, "import_otp_urls": { - "message": "Import OTP URLs", + "message": "Import OTPAuth URI", "description": "Import OTP URLs. Shown as add account method." }, + "import_drop_file": { + "message": "Drop a backup file or click to browse", + "description": "File import dropzone title" + }, + "import_file_accepts": { + "message": "Accepts .json and .txt exports", + "description": "File import dropzone hint" + }, + "import_choose_file": { + "message": "Choose file", + "description": "File import dropzone button" + }, + "import_encrypted_note": { + "message": "If the file is encrypted, you'll be asked for its passphrase after selecting it.", + "description": "File import note about encrypted backups" + }, + "import_enc_required": { + "message": "Encrypted — passphrase required", + "description": "Badge on a picked encrypted backup file" + }, + "import_pass_placeholder": { + "message": "Enter the file's passphrase", + "description": "Passphrase input placeholder for encrypted import" + }, + "import_pass_hint": { + "message": "This is the password you set when you created the backup.", + "description": "Hint under the import passphrase field" + }, + "import_decrypt": { + "message": "Decrypt & import", + "description": "Button to decrypt and import an encrypted backup" + }, "import_backup_qr_partly_failed": { "message": "Import successful, but some QR codes could not be recognized.", "description": "Import successful, but some QR image cannot be recognized." @@ -297,14 +317,110 @@ "message": "Use Autofill", "description": "Use Autofill" }, - "use_high_contrast": { - "message": "Use High Contrast", - "description": "Use High Contrast" - }, "theme": { "message": "Theme", "description": "Theme" }, + "settings_appearance": { + "message": "Appearance", + "description": "Settings section: appearance" + }, + "settings_general": { + "message": "General", + "description": "Settings section: general" + }, + "edit_accounts": { + "message": "Edit accounts", + "description": "Header title while in edit mode" + }, + "show_qr": { + "message": "Show QR code", + "description": "Context-menu action to show an account's QR code" + }, + "pin_to_top": { + "message": "Pin to top", + "description": "Context-menu action to pin an account to the top" + }, + "unpin": { + "message": "Unpin", + "description": "Context-menu action to unpin an account" + }, + "vault_locked": { + "message": "Vault locked", + "description": "Title on the unlock screen" + }, + "unlock": { + "message": "Unlock", + "description": "Unlock button" + }, + "qr_transfer_title": { + "message": "Transfer this account", + "description": "QR sheet caption title" + }, + "qr_transfer_desc": { + "message": "Scan this code with OTPilot on another device to move this account across — it never leaves your devices.", + "description": "QR sheet caption body" + }, + "backup_on_device": { + "message": "On this device", + "description": "Backup section: local" + }, + "backup_cloud_sync": { + "message": "Cloud sync", + "description": "Backup section: cloud" + }, + "backup_requires_password": { + "message": "Set a password first to use cloud backup, so your secrets are encrypted before they leave this device.", + "description": "Shown in the cloud backup section when no master password is set." + }, + "backup_connected": { + "message": "Connected", + "description": "Cloud provider connected status" + }, + "backup_not_connected": { + "message": "Not connected", + "description": "Cloud provider not-connected status" + }, + "showing_codes_for": { + "message": "Showing codes for", + "description": "Smart-filter banner prefix, followed by the site domain" + }, + "other_accounts": { + "message": "Other accounts", + "description": "Divider above non-matching accounts in the filter view" + }, + "filter_to_site": { + "message": "Filter to", + "description": "Banner prefix to re-apply the site filter, followed by the domain" + }, + "onboarding_title": { + "message": "Codes that travel with you", + "description": "Onboarding headline" + }, + "onboarding_subtitle": { + "message": "Generate secure two-factor codes for every account — encrypted, offline, and always one tap from copy.", + "description": "Onboarding subtitle" + }, + "onboarding_feature_offline": { + "message": "Works fully offline — nothing leaves your device", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_encrypted": { + "message": "Encrypted vault with optional password lock", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_scan": { + "message": "Scan a QR or import an existing backup", + "description": "Onboarding feature bullet" + }, + "onboarding_get_started": { + "message": "Get started", + "description": "Onboarding primary button" + }, + "onboarding_import": { + "message": "I have a backup to import", + "description": "Onboarding secondary button" + }, "theme_light": { "message": "Light", "description": "Light theme" @@ -313,9 +429,9 @@ "message": "Dark", "description": "Dark theme" }, - "theme_simple": { - "message": "Simple", - "description": "Simple theme" + "theme_auto": { + "message": "Auto", + "description": "Follow the system light/dark setting" }, "theme_compact": { "message": "Compact", @@ -325,14 +441,6 @@ "message": "High Contrast", "description": "High Contrast theme" }, - "theme_flat": { - "message": "Flat", - "description": "Flat theme" - }, - "storage_sync_info": { - "message": "Automatically backup your data to 3rd party storage services.", - "description": "3rd party backup info" - }, "browser_sync": { "message": "Browser Sync", "description": "Storage location" @@ -362,10 +470,6 @@ } } }, - "otp_unsupported_warn": { - "message": "You have one or more Steam or Blizzard accounts. Unencrypted backups will not use standardized backup format.", - "description": "Warning if using account that is not supported by standard backup format." - }, "otp_backup_inform": { "message": "You can import backups from some other applications.", "description": "Info text on import page" @@ -407,9 +511,6 @@ "backup": { "message": "Backup" }, - "backup_file_info": { - "message": "Backup your data to a file." - }, "password_policy_default_hint": { "message": "Your password does not meet your organization's security requirements. Contact your administrator for more information." }, @@ -489,7 +590,7 @@ "message": "Grants write-only access to the clipboard to copy codes to clipboard when you click on the account." }, "permission_context_menus": { - "message": "Adds Authenticator to context menu." + "message": "Adds OTPilot to context menu." }, "permission_sync_clock": { "message": "Allows clock sync with Google." @@ -500,18 +601,6 @@ "permission_dropbox_cannot_revoke": { "message": "You must disable Dropbox backup first." }, - "permission_drive": { - "message": "Allows backup to Google Drive." - }, - "permission_drive_cannot_revoke": { - "message": "You must disable Google Drive backup first." - }, - "permission_onedrive": { - "message": "Allows backup to OneDrive." - }, - "permission_onedrive_cannot_revoke": { - "message": "You must disable OneDrive backup first." - }, "permission_unknown_permission": { "message": "Unknown permission. If see this message, please send a bug report." }, @@ -520,5 +609,9 @@ }, "activate_auto_filter": { "message": "Warning: Smart filter loosely matches the domain name to an account. Always verify that you are on the correct website before entering a code!" + }, + "backup_unavailable": { + "message": "Not available yet", + "description": "Shown on a cloud provider that is not implemented yet." } } diff --git a/_locales/es/messages.json b/_locales/es/messages.json index 87c6775e5..f6d53cab5 100644 --- a/_locales/es/messages.json +++ b/_locales/es/messages.json @@ -1,14 +1,22 @@ { "extName": { - "message": "Autenticador", + "message": "OTPilot Authenticator", "description": "Extension Name." }, "extShortName": { - "message": "Autenticación", + "message": "OTPilot", "description": "Extension Short Name." }, + "host": { + "message": "Sitio web", + "description": "Bound website host used to restrict autofill to a matching page." + }, + "backup_requires_password": { + "message": "Primero configura una contraseña para usar la copia de seguridad en la nube, así tus claves secretas se cifrarán antes de salir de este dispositivo.", + "description": "Shown in the cloud backup section when no master password is set." + }, "extDesc": { - "message": "Authenticator genera códigos de autenticación de dos factores en su navegador.", + "message": "OTPilot Authenticator genera códigos de autenticación de dos factores (2FA), con copia de seguridad cifrada en la nube, autocompletado según el sitio web y recomendaciones de seguridad.", "description": "Extension Description." }, "added": { @@ -20,9 +28,13 @@ "description": "QR Error." }, "errorsecret": { - "message": "Clave secreta de la cuenta invalida", + "message": "Clave secreta de la cuenta inválida", "description": "Secret Error." }, + "errorissuer": { + "message": "El emisor no puede contener «::»", + "description": "Issuer Error." + }, "add_code": { "message": "Añadir cuenta", "description": "Add account." @@ -36,17 +48,13 @@ "description": "Manual Entry." }, "migration_fail": { - "message": "Error al importar. Si intentas migrar datos desde el Autenticador de Google, exporta de nuevo los datos y vuelve a intentarlo.", + "message": "Error al importar. Si estás migrando datos desde Google Authenticator, exporta de nuevo tus datos desde Google Authenticator e inténtalo otra vez.", "description": "Import migration data failed." }, "migration_partly_fail": { "message": "Los datos de algunas cuentas no se importaron correctamente.", "description": "Some migration data is broken." }, - "close": { - "message": "Cerrar", - "description": "Close." - }, "ok": { "message": "Aceptar", "description": "OK." @@ -80,7 +88,7 @@ "description": "Update Success." }, "updateFailure": { - "message": "Falla.", + "message": "Fallo.", "description": "Update Failure." }, "about": { @@ -99,10 +107,6 @@ "message": "Contraseña actual", "description": "Current Passphrase." }, - "new_phrase": { - "message": "Nueva contraseña", - "description": "New Passphrase." - }, "phrase": { "message": "Contraseña", "description": "Passphrase." @@ -112,7 +116,7 @@ "description": "Confirm Passphrase." }, "confirm_delete": { - "message": "¿Estás seguro que deseas borrar esta cuenta? Esta acción no se puede deshacer.", + "message": "¿Estás seguro de que deseas eliminar esta cuenta? Esta acción no se puede deshacer.", "description": "Remove entry confirmation" }, "confirm_delete_all": { @@ -120,21 +124,17 @@ "description": "Message that user is required to acknowledge before clearing all data." }, "delete_all": { - "message": "Restablecer auntenticador" + "message": "Restablecer OTPilot" }, "delete_all_warning": { - "message": "Esto eliminará todos tus datos y restablecerá completamente Authenticator. ¡No podrás recuperar ningún dato eliminado! Deberías considerar guardar una copia de seguridad antes de resetear Authenticator." + "message": "Esto eliminará todos tus datos y restablecerá completamente OTPilot. ¡No podrás recuperar ningún dato eliminado! Deberías considerar guardar una copia de seguridad antes de restablecer OTPilot." }, "security_warning": { "message": "Esta contraseña se utilizará para cifrar tus cuentas. Nadie puede ayudarte si olvidas la contraseña.", "description": "Passphrase Warning." }, - "update": { - "message": "Actualizar", - "description": "Update." - }, "phrase_incorrect": { - "message": "No puedes añadir una nueva cuenta hasta que todas las demás cuentas estén descifradas. Por favor introduzca la contraseña correcta antes de continuar.", + "message": "No puedes añadir una nueva cuenta hasta que todas las demás cuentas estén descifradas. Por favor, introduce la contraseña correcta antes de continuar.", "description": "Passphrase Incorrect." }, "phrase_incorrect_export": { @@ -154,35 +154,27 @@ "description": "Copied." }, "feedback": { - "message": "Su opinión", + "message": "Comentarios", "description": "Feedback." }, - "translate": { - "message": "Traducir", - "description": "Translate." - }, "source": { - "message": "Código Fuente", + "message": "Código fuente", "description": "Source Code." }, "passphrase_info": { - "message": "Introduzca la contraseña para descifrar los datos de la cuenta.", + "message": "Introduce la contraseña para descifrar los datos de la cuenta.", "description": "Passphrase Info" }, "sync_clock": { "message": "Sincronizar el reloj con Google", "description": "Sync Clock" }, - "remember_phrase": { - "message": "Recordar la contraseña", - "description": "Remember Passphrase" - }, "clock_too_far_off": { - "message": "¡Peligro! Tu reloj local está demasiado desfasado, corrígelo antes de continuar.", + "message": "¡Atención! Tu reloj local está demasiado desfasado, corrígelo antes de continuar.", "description": "Local Time is Too Far Off" }, "remind_backup": { - "message": "¿Tienes una copia de seguridad cuentas¡No esperes hasta que sea demasiado tarde!", + "message": "¿Tienes una copia de seguridad de tus cuentas? ¡No esperes hasta que sea demasiado tarde!", "description": "Remind Backup" }, "capture_failed": { @@ -210,7 +202,7 @@ "description": "Scale" }, "export_info": { - "message": "Advertencia: todas las copias de seguridad están sin encriptar. ¿Quieres añadir una cuenta para otra aplicación? Pasa el ratón por encima de la esquina superior-derecha de cualquier cuenta y pulsa el botón oculto.", + "message": "Advertencia: todas las copias de seguridad están sin cifrar. ¿Quieres añadir una cuenta a otra aplicación? Pasa el ratón por encima de la esquina superior derecha de cualquier cuenta y pulsa el botón oculto.", "description": "Export menu info text" }, "download_backup": { @@ -230,17 +222,49 @@ "description": "Import qr image backup." }, "import_qr_images": { - "message": "Import QR Images", + "message": "Importar imágenes QR", "description": "Import qr images. Shown as add account method." }, "import_backup_code": { - "message": "Importar el Texto de la Copia de Seguridad", + "message": "Importar URI de OTPAuth", "description": "Import backup code." }, "import_otp_urls": { - "message": "Import OTP URLs", + "message": "Importar URI de OTPAuth", "description": "Import OTP URLs. Shown as add account method." }, + "import_drop_file": { + "message": "Suelta un archivo de copia de seguridad o haz clic para explorar", + "description": "File import dropzone title" + }, + "import_file_accepts": { + "message": "Acepta archivos exportados .json y .txt", + "description": "File import dropzone hint" + }, + "import_choose_file": { + "message": "Elegir archivo", + "description": "File import dropzone button" + }, + "import_encrypted_note": { + "message": "Si el archivo está cifrado, se te pedirá su contraseña después de seleccionarlo.", + "description": "File import note about encrypted backups" + }, + "import_enc_required": { + "message": "Cifrado: se requiere contraseña", + "description": "Badge on a picked encrypted backup file" + }, + "import_pass_placeholder": { + "message": "Introduce la contraseña del archivo", + "description": "Passphrase input placeholder for encrypted import" + }, + "import_pass_hint": { + "message": "Esta es la contraseña que estableciste al crear la copia de seguridad.", + "description": "Hint under the import passphrase field" + }, + "import_decrypt": { + "message": "Descifrar e importar", + "description": "Button to decrypt and import an encrypted backup" + }, "import_backup_qr_partly_failed": { "message": "Se importó correctamente, sin embargo, algunas imágenes QR no pudieron ser reconocidas.", "description": "Import successful, but some QR image cannot be recognized." @@ -254,7 +278,7 @@ "description": "Show all entries." }, "dropbox_risk": { - "message": "Advertencia: las copias de seguridad no están encriptadas. Lo cuál no se recomienda, pero es bajo tu propio riesgo.", + "message": "Advertencia: las copias de seguridad no están cifradas. Úsalas bajo tu propia responsabilidad.", "description": "Backup risk warning." }, "import_error_password": { @@ -294,17 +318,109 @@ "description": "Manual backup" }, "use_autofill": { - "message": "Usar auto-completado", + "message": "Usar autocompletado", "description": "Use Autofill" }, - "use_high_contrast": { - "message": "Utilizar contraste alto", - "description": "Use High Contrast" - }, "theme": { "message": "Tema", "description": "Theme" }, + "settings_appearance": { + "message": "Apariencia", + "description": "Settings section: appearance" + }, + "settings_general": { + "message": "General", + "description": "Settings section: general" + }, + "edit_accounts": { + "message": "Editar cuentas", + "description": "Header title while in edit mode" + }, + "show_qr": { + "message": "Mostrar código QR", + "description": "Context-menu action to show an account's QR code" + }, + "pin_to_top": { + "message": "Anclar arriba", + "description": "Context-menu action to pin an account to the top" + }, + "unpin": { + "message": "Desanclar", + "description": "Context-menu action to unpin an account" + }, + "vault_locked": { + "message": "Bóveda bloqueada", + "description": "Title on the unlock screen" + }, + "unlock": { + "message": "Desbloquear", + "description": "Unlock button" + }, + "qr_transfer_title": { + "message": "Transferir esta cuenta", + "description": "QR sheet caption title" + }, + "qr_transfer_desc": { + "message": "Escanea este código con OTPilot en otro dispositivo para transferir esta cuenta; nunca sale de tus dispositivos.", + "description": "QR sheet caption body" + }, + "backup_on_device": { + "message": "En este dispositivo", + "description": "Backup section: local" + }, + "backup_cloud_sync": { + "message": "Sincronización en la nube", + "description": "Backup section: cloud" + }, + "backup_connected": { + "message": "Conectado", + "description": "Cloud provider connected status" + }, + "backup_not_connected": { + "message": "No conectado", + "description": "Cloud provider not-connected status" + }, + "showing_codes_for": { + "message": "Mostrando códigos para", + "description": "Smart-filter banner prefix, followed by the site domain" + }, + "other_accounts": { + "message": "Otras cuentas", + "description": "Divider above non-matching accounts in the filter view" + }, + "filter_to_site": { + "message": "Filtrar por", + "description": "Banner prefix to re-apply the site filter, followed by the domain" + }, + "onboarding_title": { + "message": "Códigos que viajan contigo", + "description": "Onboarding headline" + }, + "onboarding_subtitle": { + "message": "Genera códigos de doble factor seguros para cada cuenta: cifrados, sin conexión y siempre a un toque de copiarlos.", + "description": "Onboarding subtitle" + }, + "onboarding_feature_offline": { + "message": "Funciona totalmente sin conexión: nada sale de tu dispositivo", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_encrypted": { + "message": "Bóveda cifrada con bloqueo por contraseña opcional", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_scan": { + "message": "Escanea un código QR o importa una copia de seguridad existente", + "description": "Onboarding feature bullet" + }, + "onboarding_get_started": { + "message": "Comenzar", + "description": "Onboarding primary button" + }, + "onboarding_import": { + "message": "Tengo una copia de seguridad para importar", + "description": "Onboarding secondary button" + }, "theme_light": { "message": "Claro", "description": "Light theme" @@ -313,9 +429,9 @@ "message": "Oscuro", "description": "Dark theme" }, - "theme_simple": { - "message": "Simple", - "description": "Simple theme" + "theme_auto": { + "message": "Automático", + "description": "Follow the system light/dark setting" }, "theme_compact": { "message": "Compacto", @@ -325,14 +441,6 @@ "message": "Alto contraste", "description": "High Contrast theme" }, - "theme_flat": { - "message": "Plano", - "description": "Flat theme" - }, - "storage_sync_info": { - "message": "Realizar copias de seguridad automáticas de tus datos en servicios de almacenamiento de terceros.", - "description": "3rd party backup info" - }, "browser_sync": { "message": "Sincronización con el navegador", "description": "Storage location" @@ -353,7 +461,7 @@ "description": "Sign out of 3rd party storage services" }, "token_revoked": { - "message": "Ha habido un problema al conectarse a tu cuenta $SERVICE$. Inténtalo de nuevo.", + "message": "Ha habido un problema al conectar con tu cuenta de $SERVICE$. Vuelve a iniciar sesión.", "description": "Error authenticating to backup service. $SERVICE$ will be replaced with a proper noun (E.g.: 'Google Drive' or 'Dropbox')", "placeholders": { "service": { @@ -362,16 +470,12 @@ } } }, - "otp_unsupported_warn": { - "message": "Tienes una o más cuentas de Steam o Blizzard. Las copias de seguridad no cifradas no utilizarán el formato de copia de seguridad estandarizado.", - "description": "Warning if using account that is not supported by standard backup format." - }, "otp_backup_inform": { "message": "Puedes importar copias de seguridad desde otras aplicaciones.", "description": "Info text on import page" }, "otp_backup_learn": { - "message": "Aprende más", + "message": "Más información", "description": "learn more link on import page. Placed after otp_backup_inform" }, "loading": { @@ -407,9 +511,6 @@ "backup": { "message": "Copia de seguridad" }, - "backup_file_info": { - "message": "Guarda en un archivo una copia de seguridad de tus datos." - }, "password_policy_default_hint": { "message": "La contraseña no cumple los requisitos de seguridad de tu organización. Para obtener más información, ponte en contacto con el administrador." }, @@ -477,19 +578,19 @@ "message": "Acceder al almacenamiento del navegador para guardar los datos de la cuenta." }, "permission_identity": { - "message": "Permitir iniciar sesión en servicios para almacenamiento de terceros." + "message": "Permitir iniciar sesión en servicios de almacenamiento de terceros." }, "permission_alarms": { - "message": "Allows auto-lock to work." + "message": "Habilitar el bloqueo automático." }, "permission_scripting": { - "message": "Inject scripts into he current tab to scan QR codes and allow auto-fill to work." + "message": "Insertar scripts en la pestaña actual para escanear códigos QR y permitir que funcione el autocompletado." }, "permission_clipboard_write": { - "message": "Otorga acceso de sólo escritura al portapapeles para copiar codigos al portapapeles cuando haga clic en la cuenta." + "message": "Otorgar acceso de solo escritura al portapapeles para copiar los códigos al hacer clic en la cuenta." }, "permission_context_menus": { - "message": "Agregar Authenticator al menú contextual." + "message": "Agregar OTPilot al menú contextual." }, "permission_sync_clock": { "message": "Habilitar sincronización del reloj con Google." @@ -498,27 +599,19 @@ "message": "Habilitar copias de seguridad con Dropbox." }, "permission_dropbox_cannot_revoke": { - "message": "Primero debe deshabilitar la copia de seguridad de Dropbox." - }, - "permission_drive": { - "message": "Permitir copias de seguridad de Google Drive." - }, - "permission_drive_cannot_revoke": { - "message": "Primero debes desactivar la copia de seguridad de Google Drive." - }, - "permission_onedrive": { - "message": "Permite la copia de seguridad a OneDrive." - }, - "permission_onedrive_cannot_revoke": { - "message": "Primero debes desactivar la copia de seguridad de OneDrive." + "message": "Primero debes deshabilitar la copia de seguridad de Dropbox." }, "permission_unknown_permission": { - "message": "Permiso desconocido. Si ve este mensaje, por favor envíe un informe de error." + "message": "Permiso desconocido. Si ves este mensaje, por favor, envía un informe de error." }, "phrase_wrong": { - "message": "Password incorrect" + "message": "Contraseña incorrecta" }, "activate_auto_filter": { - "message": "Warning: Smart filter loosely matches the domain name to an account. Always verify that you are on the correct website before entering a code!" + "message": "Advertencia: el filtro inteligente relaciona el nombre de dominio con una cuenta de forma aproximada. ¡Verifica siempre que estás en el sitio web correcto antes de introducir un código!" + }, + "backup_unavailable": { + "message": "No disponible todavía", + "description": "Shown on a cloud provider that is not implemented yet." } } diff --git a/_locales/et/messages.json b/_locales/et/messages.json index 802df3bb0..60a421cf8 100644 --- a/_locales/et/messages.json +++ b/_locales/et/messages.json @@ -1,14 +1,22 @@ { "extName": { - "message": "Autentikaator", + "message": "OTPilot Authenticator", "description": "Extension Name." }, "extShortName": { - "message": "Autentikaator", + "message": "OTPilot", "description": "Extension Short Name." }, + "host": { + "message": "Veebisait", + "description": "Bound website host used to restrict autofill to a matching page." + }, + "backup_requires_password": { + "message": "Pilvvarunduse kasutamiseks seadistage kõigepealt salasõna – nii krüpteeritakse teie saladused enne seadmest lahkumist.", + "description": "Shown in the cloud backup section when no master password is set." + }, "extDesc": { - "message": "Autentikaator genereerib teie brauseris mitmikautentimise koode.", + "message": "OTPilot Authenticator genereerib kahefaktorilisi (2FA) koode ning pakub krüpteeritud pilvvarundust, saidipõhist automaattäitmist ja turvasoovitusi.", "description": "Extension Description." }, "added": { @@ -23,6 +31,10 @@ "message": "Kehtetu konto saladus", "description": "Secret Error." }, + "errorissuer": { + "message": "Väljastaja ei tohi sisaldada märki \"::\"", + "description": "Issuer Error." + }, "add_code": { "message": "Lisa konto", "description": "Add account." @@ -43,10 +55,6 @@ "message": "Osa konto andmeid ei õnnestunud importida.", "description": "Some migration data is broken." }, - "close": { - "message": "Sulge", - "description": "Close." - }, "ok": { "message": "Olgu", "description": "OK." @@ -99,10 +107,6 @@ "message": "Kehtiv salasõna", "description": "Current Passphrase." }, - "new_phrase": { - "message": "Uus salasõna", - "description": "New Passphrase." - }, "phrase": { "message": "Salasõna", "description": "Passphrase." @@ -120,19 +124,15 @@ "description": "Message that user is required to acknowledge before clearing all data." }, "delete_all": { - "message": "Lähtesta autentikaator" + "message": "Lähtesta OTPilot" }, "delete_all_warning": { - "message": "See kustutab kõik teie andmed ja lähtestab Autentikaatori täielikult. Kustutatud andmeid ei saa taastada! Enne Autentikaatori lähtestamist peaksite kaaluma varukoopia loomist." + "message": "See kustutab kõik teie andmed ja lähtestab OTPiloti täielikult. Kustutatud andmeid ei saa taastada! Enne OTPiloti lähtestamist peaksite kaaluma varukoopia loomist." }, "security_warning": { "message": "Seda salasõna kasutatakse teie kontode krüpteerimiseks. Kui unustate salasõna, ei saa keegi teid aidata.", "description": "Passphrase Warning." }, - "update": { - "message": "Uuenda", - "description": "Update." - }, "phrase_incorrect": { "message": "Uut kontot ei saa lisada kuni kõik kontod on dekrüpteeritud. Palun sisestage enne jätkamist õige salasõna.", "description": "Passphrase Incorrect." @@ -157,10 +157,6 @@ "message": "Tagasiside", "description": "Feedback." }, - "translate": { - "message": "Tõlkimine", - "description": "Translate." - }, "source": { "message": "Lähtekood", "description": "Source Code." @@ -170,15 +166,11 @@ "description": "Passphrase Info" }, "sync_clock": { - "message": "Sünkrooni kell Google'ga", + "message": "Sünkrooni kell Google'iga", "description": "Sync Clock" }, - "remember_phrase": { - "message": "Mäleta salasõna", - "description": "Remember Passphrase" - }, "clock_too_far_off": { - "message": "Ettevaatust! Teie kohalik kell liiga mööda, palun parandage enne jätkamist.", + "message": "Ettevaatust! Teie seadme kell on liiga suure nihkega, palun parandage see enne jätkamist.", "description": "Local Time is Too Far Off" }, "remind_backup": { @@ -190,7 +182,7 @@ "description": "Capture Failed" }, "capture_local_file_failed": { - "message": "Are you trying to scan QR code from a local file? Use Import QR Image Backup instead.", + "message": "Kas proovite skaneerida QR koodi kohalikust failist? Kasutage selle asemel valikut „Impordi QR pildi varukoopia“.", "description": "Import QR image backup instead of scan local image" }, "based_on_time": { @@ -210,7 +202,7 @@ "description": "Scale" }, "export_info": { - "message": "Hoiatus: Kõik varukoopiad on krüpteerimata. Kas soovite lisada konto mõnda teise rakendusse? Suunake kursor mis tahes paremal ülaosas asuvale kontole ja vajutage peidetud nupule.", + "message": "Hoiatus: kõik varukoopiad on krüpteerimata. Kas soovite lisada konto mõnda teise rakendusse? Viige kursor mis tahes konto paremasse ülanurka ja klõpsake peidetud nuppu.", "description": "Export menu info text" }, "download_backup": { @@ -230,19 +222,51 @@ "description": "Import qr image backup." }, "import_qr_images": { - "message": "Import QR Images", + "message": "Impordi QR pildid", "description": "Import qr images. Shown as add account method." }, "import_backup_code": { - "message": "Impordi tekst-varukoopia", + "message": "Impordi OTPAuth URI", "description": "Import backup code." }, "import_otp_urls": { - "message": "Import OTP URLs", + "message": "Impordi OTPAuth URI", "description": "Import OTP URLs. Shown as add account method." }, + "import_drop_file": { + "message": "Lohistage varukoopia fail siia või klõpsake failide sirvimiseks", + "description": "File import dropzone title" + }, + "import_file_accepts": { + "message": "Toetab .json ja .txt eksportfaile", + "description": "File import dropzone hint" + }, + "import_choose_file": { + "message": "Vali fail", + "description": "File import dropzone button" + }, + "import_encrypted_note": { + "message": "Kui fail on krüpteeritud, küsitakse selle valimisel salasõna.", + "description": "File import note about encrypted backups" + }, + "import_enc_required": { + "message": "Krüpteeritud — vajalik on salasõna", + "description": "Badge on a picked encrypted backup file" + }, + "import_pass_placeholder": { + "message": "Sisestage faili salasõna", + "description": "Passphrase input placeholder for encrypted import" + }, + "import_pass_hint": { + "message": "See on salasõna, mille määrasite varukoopia loomisel.", + "description": "Hint under the import passphrase field" + }, + "import_decrypt": { + "message": "Dekrüpteeri ja impordi", + "description": "Button to decrypt and import an encrypted backup" + }, "import_backup_qr_partly_failed": { - "message": "Importimine õnnestus, kuid mõnesid QR'i koode ei suudetud tuvastada.", + "message": "Importimine õnnestus, kuid mõningaid QR-koode ei suudetud tuvastada.", "description": "Import successful, but some QR image cannot be recognized." }, "import_backup_qr_in_batches": { @@ -270,7 +294,7 @@ "description": "Remove password." }, "download_enc_backup": { - "message": "Laadi alla parooliga kaitstud varukoopia", + "message": "Laadi alla salasõnaga kaitstud varukoopia", "description": "Download Encrypted Backup" }, "search": { @@ -297,14 +321,106 @@ "message": "Automaatne täitmine", "description": "Use Autofill" }, - "use_high_contrast": { - "message": "Kõrge kontrastsus", - "description": "Use High Contrast" - }, "theme": { "message": "Teema", "description": "Theme" }, + "settings_appearance": { + "message": "Välimus", + "description": "Settings section: appearance" + }, + "settings_general": { + "message": "Üldine", + "description": "Settings section: general" + }, + "edit_accounts": { + "message": "Muuda kontosid", + "description": "Header title while in edit mode" + }, + "show_qr": { + "message": "Kuva QR kood", + "description": "Context-menu action to show an account's QR code" + }, + "pin_to_top": { + "message": "Kinnita algusesse", + "description": "Context-menu action to pin an account to the top" + }, + "unpin": { + "message": "Eemalda kinnitus", + "description": "Context-menu action to unpin an account" + }, + "vault_locked": { + "message": "Hoidla lukustatud", + "description": "Title on the unlock screen" + }, + "unlock": { + "message": "Ava lukustus", + "description": "Unlock button" + }, + "qr_transfer_title": { + "message": "Kanna see konto üle", + "description": "QR sheet caption title" + }, + "qr_transfer_desc": { + "message": "Skaneerige see kood OTPilotiga teises seadmes, et see konto üle kanda — see ei lahku kunagi teie seadmetest.", + "description": "QR sheet caption body" + }, + "backup_on_device": { + "message": "Selles seadmes", + "description": "Backup section: local" + }, + "backup_cloud_sync": { + "message": "Pilvsünkroonimine", + "description": "Backup section: cloud" + }, + "backup_connected": { + "message": "Ühendatud", + "description": "Cloud provider connected status" + }, + "backup_not_connected": { + "message": "Pole ühendatud", + "description": "Cloud provider not-connected status" + }, + "showing_codes_for": { + "message": "Kuvatavad koodid saidile", + "description": "Smart-filter banner prefix, followed by the site domain" + }, + "other_accounts": { + "message": "Muud kontod", + "description": "Divider above non-matching accounts in the filter view" + }, + "filter_to_site": { + "message": "Filtreeri saidile", + "description": "Banner prefix to re-apply the site filter, followed by the domain" + }, + "onboarding_title": { + "message": "Koodid, mis on alati kaasas", + "description": "Onboarding headline" + }, + "onboarding_subtitle": { + "message": "Genereeri turvalisi kaheastmelisi koode iga konto jaoks — krüpteeritud, võrguühenduseta ja alati ühe puudutuse kaugusel kopeerimisest.", + "description": "Onboarding subtitle" + }, + "onboarding_feature_offline": { + "message": "Töötab täielikult võrguühenduseta — miski ei lahku teie seadmest", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_encrypted": { + "message": "Krüpteeritud hoidla valikulise salasõnalukuga", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_scan": { + "message": "Skaneeri QR kood või impordi olemasolev varukoopia", + "description": "Onboarding feature bullet" + }, + "onboarding_get_started": { + "message": "Alusta", + "description": "Onboarding primary button" + }, + "onboarding_import": { + "message": "Mul on varukoopia, mida importida", + "description": "Onboarding secondary button" + }, "theme_light": { "message": "Hele", "description": "Light theme" @@ -313,9 +429,9 @@ "message": "Tume", "description": "Dark theme" }, - "theme_simple": { - "message": "Lihtne", - "description": "Simple theme" + "theme_auto": { + "message": "Automaatne", + "description": "Follow the system light/dark setting" }, "theme_compact": { "message": "Kompaktne", @@ -325,16 +441,8 @@ "message": "Kõrge kontrastsus", "description": "High Contrast theme" }, - "theme_flat": { - "message": "Tasane", - "description": "Flat theme" - }, - "storage_sync_info": { - "message": "Varundage oma andmed automaatselt kolmanda osapoole salvestusteenustesse.", - "description": "3rd party backup info" - }, "browser_sync": { - "message": "Brauseri sünk", + "message": "Brauseri sünkroonimine", "description": "Storage location" }, "sign_in": { @@ -353,7 +461,7 @@ "description": "Sign out of 3rd party storage services" }, "token_revoked": { - "message": "Teie kontoga $SERVICE$ ühendamisel tekkis probleem, palun logige uuesti sisse.", + "message": "$SERVICE$ kontoga ühendamisel tekkis probleem, palun logige uuesti sisse.", "description": "Error authenticating to backup service. $SERVICE$ will be replaced with a proper noun (E.g.: 'Google Drive' or 'Dropbox')", "placeholders": { "service": { @@ -362,12 +470,8 @@ } } }, - "otp_unsupported_warn": { - "message": "Teil on üks või mitu Steami või Blizzardi kontot. Krüpteerimata varukoopiad ei kasuta standardiseeritud varundusvormingut.", - "description": "Warning if using account that is not supported by standard backup format." - }, "otp_backup_inform": { - "message": "Varukoopiaid saate importida ka mõnedest teisest rakendustest.", + "message": "Varukoopiaid saate importida ka mõnedest teistest rakendustest.", "description": "Info text on import page" }, "otp_backup_learn": { @@ -407,11 +511,8 @@ "backup": { "message": "Varundus" }, - "backup_file_info": { - "message": "Varundage enda andmed failiks." - }, "password_policy_default_hint": { - "message": "Teie salasõna ei vasta teie organisatsiooni turvanõuetele. Lisateabe saamiseks kontakteeruge oma administraatoriga." + "message": "Teie salasõna ei vasta teie organisatsiooni turvanõuetele. Lisateabe saamiseks võtke ühendust oma administraatoriga." }, "advisor": { "message": "Nõuandja" @@ -420,22 +521,22 @@ "message": "Määrake enda andmete kaitsmiseks salasõna." }, "advisor_insight_auto_lock_not_set": { - "message": "Enable auto-lock to protect your data." + "message": "Lülitage sisse automaatne lukustus, et kaitsta oma andmeid." }, "advisor_insight_browser_sync_not_enabled": { - "message": "Browser sync is disabled. Enabling it allows accounts to be synced across browsers." + "message": "Brauseri sünkroonimine on välja lülitatud. Selle sisselülitamine võimaldab kontosid brauserite vahel sünkroonida." }, "advisor_insight_auto_fill_not_enabled": { - "message": "Autofill can be enabled to automatically fill codes into websites." + "message": "Automaatse täitmise sisselülitamisel täidetakse koodid veebisaitidele automaatselt." }, "advisor_insight_smart_filter_not_enabled": { - "message": "Enabling smart filter allows for quick access to accounts." + "message": "Nutika filtri sisselülitamine võimaldab kontodele kiiret ligipääsu." }, "show_all_insights": { - "message": "Show all insights." + "message": "Kuva kõik soovitused." }, "no_insight_available": { - "message": "No insights found, everything looks good!" + "message": "Soovitusi ei leitud, kõik on korras!" }, "danger": { "message": "Oht" @@ -453,7 +554,7 @@ "message": "Lisateave" }, "enable_context_menu": { - "message": "Add to context menu" + "message": "Lisa kontekstimenüüsse" }, "no_entires": { "message": "Pole kontosid, mida kuvada. Lisage enda esimene konto." @@ -465,60 +566,52 @@ "message": "Võta tagasi" }, "permission_show_required_permissions": { - "message": "Show non-revocable permissions" + "message": "Kuva kohustuslikud õigused" }, "permission_required": { - "message": "This is a required permission and cannot be revoked." + "message": "See on kohustuslik õigus, mida ei saa tühistada." }, "permission_active_tab": { - "message": "Access to the current tab to scan QR codes." + "message": "Ligipääs praegusele vahekaardile QR koodide skaneerimiseks." }, "permission_storage": { - "message": "Access to browser storage to store account data." + "message": "Ligipääs brauseri salvestusruumile kontoandmete salvestamiseks." }, "permission_identity": { - "message": "Allows sign in to 3rd party storage services." + "message": "Võimaldab sisse logida kolmandate osapoolte salvestusteenustesse." }, "permission_alarms": { - "message": "Allows auto-lock to work." + "message": "Võimaldab automaatse lukustuse toimimist." }, "permission_scripting": { - "message": "Inject scripts into he current tab to scan QR codes and allow auto-fill to work." + "message": "Süstib skripte praegusesse vahekaarti, et skaneerida QR koode ja võimaldada automaatse täitmise toimimist." }, "permission_clipboard_write": { - "message": "Grants write-only access to the clipboard to copy codes to clipboard when you click on the account." + "message": "Annab lõikelauale ainult kirjutamisõiguse, et kontol klõpsates saaks koodi lõikelauale kopeerida." }, "permission_context_menus": { - "message": "Adds Authenticator to context menu." + "message": "Lisab OTPiloti kontekstimenüüsse." }, "permission_sync_clock": { - "message": "Allows clock sync with Google." + "message": "Võimaldab kella sünkroonimist Google'iga." }, "permission_dropbox": { - "message": "Allows backup to Dropbox." + "message": "Võimaldab varundada Dropboxisse." }, "permission_dropbox_cannot_revoke": { - "message": "You must disable Dropbox backup first." - }, - "permission_drive": { - "message": "Allows backup to Google Drive." - }, - "permission_drive_cannot_revoke": { - "message": "You must disable Google Drive backup first." - }, - "permission_onedrive": { - "message": "Allows backup to OneDrive." - }, - "permission_onedrive_cannot_revoke": { - "message": "You must disable OneDrive backup first." + "message": "Peate kõigepealt Dropboxi varunduse välja lülitama." }, "permission_unknown_permission": { - "message": "Unknown permission. If see this message, please send a bug report." + "message": "Tundmatu õigus. Kui näete seda teadet, palun saatke vearaport." }, "phrase_wrong": { - "message": "Password incorrect" + "message": "Vale salasõna" }, "activate_auto_filter": { - "message": "Warning: Smart filter loosely matches the domain name to an account. Always verify that you are on the correct website before entering a code!" + "message": "Hoiatus: nutikas filter sobitab domeeninime kontoga ligikaudselt. Enne koodi sisestamist kontrollige alati, et olete õigel veebisaidil!" + }, + "backup_unavailable": { + "message": "Pole veel saadaval", + "description": "Shown on a cloud provider that is not implemented yet." } } diff --git a/_locales/fa/messages.json b/_locales/fa/messages.json index d2f67ac13..2adf9fe46 100644 --- a/_locales/fa/messages.json +++ b/_locales/fa/messages.json @@ -1,14 +1,22 @@ { "extName": { - "message": "Authenticator", + "message": "OTPilot Authenticator", "description": "Extension Name." }, "extShortName": { - "message": "احراز هویت کننده", + "message": "OTPilot", "description": "Extension Short Name." }, + "host": { + "message": "وب‌سایت", + "description": "Bound website host used to restrict autofill to a matching page." + }, + "backup_requires_password": { + "message": "ابتدا یک کلمه عبور تنظیم کنید تا از پشتیبان‌گیری ابری استفاده کنید؛ به این ترتیب رمزهای شما پیش از خروج از این دستگاه رمزنگاری می‌شوند.", + "description": "Shown in the cloud backup section when no master password is set." + }, "extDesc": { - "message": "افزونه Authenticator کدهای تأیید هویت دو مرحله‌ای را در مرورگر شما تولید می‌کند.", + "message": "OTPilot Authenticator کدهای تأیید دو مرحله‌ای (2FA) تولید می‌کند؛ همراه با پشتیبان‌گیری ابری رمزنگاری‌شده، تکمیل خودکار مطابق با وب‌سایت، و توصیه‌های امنیتی.", "description": "Extension Description." }, "added": { @@ -23,6 +31,10 @@ "message": "رمز حساب نامعتبر است", "description": "Secret Error." }, + "errorissuer": { + "message": "صادرکننده نمی‌تواند شامل «::» باشد", + "description": "Issuer Error." + }, "add_code": { "message": "افزودن حساب", "description": "Add account." @@ -32,7 +44,7 @@ "description": "Scan QR Code." }, "add_secret": { - "message": "وارد کردن بصورت دستی", + "message": "ورود دستی", "description": "Manual Entry." }, "migration_fail": { @@ -43,10 +55,6 @@ "message": "برخی داده‌های حساب به طور موفق وارد نشدند.", "description": "Some migration data is broken." }, - "close": { - "message": "بستن", - "description": "Close." - }, "ok": { "message": "تایید", "description": "OK." @@ -99,10 +107,6 @@ "message": "کلمه عبور فعلی", "description": "Current Passphrase." }, - "new_phrase": { - "message": "کلمه عبور جدید", - "description": "New Passphrase." - }, "phrase": { "message": "کلمه عبور", "description": "Passphrase." @@ -120,21 +124,17 @@ "description": "Message that user is required to acknowledge before clearing all data." }, "delete_all": { - "message": "بازنشانی Authenticator" + "message": "بازنشانی OTPilot" }, "delete_all_warning": { - "message": "این تمام داده‌های شما را حذف کرده و افزونه Authenticator را به طور کامل بازنشانی می‌کند. شما بعداً قادر نخواهید بود داده‌های حذف شده را بازیابی کنید! شما باید قبل از بازنشانی افزونه Authenticator از داده‌های خود نسخه پشتیبان تهیه کنید." + "message": "این تمام داده‌های شما را حذف کرده و افزونه OTPilot را به طور کامل بازنشانی می‌کند. شما بعداً قادر نخواهید بود داده‌های حذف شده را بازیابی کنید! شما باید قبل از بازنشانی افزونه OTPilot از داده‌های خود نسخه پشتیبان تهیه کنید." }, "security_warning": { "message": "این کلمه عبور برای رمزنگاری حساب‌های شما استفاده خواهد شد. اگر این کلمه عبور را فراموش کنید راه دیگری برای بازگردانی آن وجود ندارد.", "description": "Passphrase Warning." }, - "update": { - "message": "به‌روز رسانی", - "description": "Update." - }, "phrase_incorrect": { - "message": "تا رمزگشایی همه حساب ها ، نمی توانید حساب جدیدی اضافه کنید. لطفا قبل از ادامه رمز عبور صحیح را وارد کنید.", + "message": "تا زمانی که همهٔ حساب‌ها رمزگشایی نشوند، نمی‌توانید حساب جدیدی اضافه کنید. لطفاً پیش از ادامه، کلمه عبور صحیح را وارد کنید.", "description": "Passphrase Incorrect." }, "phrase_incorrect_export": { @@ -146,7 +146,7 @@ "description": "Passphrase Not Match." }, "encrypted": { - "message": "رمزگذاری شده", + "message": "رمزنگاری‌شده", "description": "Encrypted." }, "copied": { @@ -154,13 +154,9 @@ "description": "Copied." }, "feedback": { - "message": "ﺑﺎﺯﺧﻮﺭﺩ", + "message": "بازخورد", "description": "Feedback." }, - "translate": { - "message": "ترجمه", - "description": "Translate." - }, "source": { "message": "کد منبع", "description": "Source Code." @@ -170,13 +166,9 @@ "description": "Passphrase Info" }, "sync_clock": { - "message": "همگام‌سازی ساعت با گوگل", + "message": "همگام‌سازی ساعت با Google", "description": "Sync Clock" }, - "remember_phrase": { - "message": "به خاطر سپردن کلمه عبور", - "description": "Remember Passphrase" - }, "clock_too_far_off": { "message": "اخطار! ساعت محلی شما نادرست است، لطفا قبل از ادامه آن را تصحیح نمایید.", "description": "Local Time is Too Far Off" @@ -190,7 +182,7 @@ "description": "Capture Failed" }, "capture_local_file_failed": { - "message": "آیا شما میخواهید رمزینه را از روی یک فایل در دستگاه خود اسکن کنید؟ بجای آن از گزینه ورود عکس پشتیبان استفاده کنید.", + "message": "آیا می‌خواهید کد QR را از یک فایل روی دستگاه خود اسکن کنید؟ به‌جای آن از گزینه «وارد کردن نسخه پشتیبان تصویر QR» استفاده کنید.", "description": "Import QR image backup instead of scan local image" }, "based_on_time": { @@ -210,7 +202,7 @@ "description": "Scale" }, "export_info": { - "message": "هشدار: تمام نسخه‌های پشتیبان رمزنگاری شده‌اند. آیا می‌خواهید یک حساب را به اپلیکیشن دیگری اضافه کنید؟ به قسمت سمت راست و بالای هر حساب رفته و دکمه مخفی را کلیک کنید.", + "message": "هشدار: تمام نسخه‌های پشتیبان رمزنگاری نشده‌اند. آیا می‌خواهید یک حساب را به اپلیکیشن دیگری اضافه کنید؟ به قسمت سمت راست و بالای هر حساب رفته و دکمه مخفی را کلیک کنید.", "description": "Export menu info text" }, "download_backup": { @@ -218,7 +210,7 @@ "description": "Download backup file." }, "import_backup": { - "message": "ایمپورت کردن فایل پشتیبان", + "message": "وارد کردن پشتیبان", "description": "Import backup." }, "import_backup_file": { @@ -234,13 +226,45 @@ "description": "Import qr images. Shown as add account method." }, "import_backup_code": { - "message": "وارد کردن پشتیبان متنی", + "message": "وارد کردن OTPAuth URI", "description": "Import backup code." }, "import_otp_urls": { - "message": "URL های OTP را وارد کنید", + "message": "وارد کردن OTPAuth URI", "description": "Import OTP URLs. Shown as add account method." }, + "import_drop_file": { + "message": "فایل پشتیبان را رها کنید یا برای مرور کلیک کنید", + "description": "File import dropzone title" + }, + "import_file_accepts": { + "message": "فایل‌های خروجی .json و .txt پذیرفته می‌شوند", + "description": "File import dropzone hint" + }, + "import_choose_file": { + "message": "انتخاب فایل", + "description": "File import dropzone button" + }, + "import_encrypted_note": { + "message": "اگر فایل رمزنگاری شده باشد، پس از انتخاب آن کلمه عبورش از شما پرسیده خواهد شد.", + "description": "File import note about encrypted backups" + }, + "import_enc_required": { + "message": "رمزنگاری شده — نیاز به کلمه عبور", + "description": "Badge on a picked encrypted backup file" + }, + "import_pass_placeholder": { + "message": "کلمه عبور فایل را وارد کنید", + "description": "Passphrase input placeholder for encrypted import" + }, + "import_pass_hint": { + "message": "این همان کلمه عبوری است که هنگام ایجاد نسخه پشتیبان تنظیم کرده‌اید.", + "description": "Hint under the import passphrase field" + }, + "import_decrypt": { + "message": "رمزگشایی و وارد کردن", + "description": "Button to decrypt and import an encrypted backup" + }, "import_backup_qr_partly_failed": { "message": "درون ریزی با موفقیت انجام شد، اما برخی از کدهای QR شناسایی نشدند.", "description": "Import successful, but some QR image cannot be recognized." @@ -297,14 +321,106 @@ "message": "استفاده از قابلیت تکمیل خودکار", "description": "Use Autofill" }, - "use_high_contrast": { - "message": "استفاده از کنتراست بالا", - "description": "Use High Contrast" - }, "theme": { "message": "تم", "description": "Theme" }, + "settings_appearance": { + "message": "ظاهر", + "description": "Settings section: appearance" + }, + "settings_general": { + "message": "عمومی", + "description": "Settings section: general" + }, + "edit_accounts": { + "message": "ویرایش حساب‌ها", + "description": "Header title while in edit mode" + }, + "show_qr": { + "message": "نمایش QR Code", + "description": "Context-menu action to show an account's QR code" + }, + "pin_to_top": { + "message": "سنجاق کردن به بالا", + "description": "Context-menu action to pin an account to the top" + }, + "unpin": { + "message": "لغو سنجاق", + "description": "Context-menu action to unpin an account" + }, + "vault_locked": { + "message": "خزانه قفل شده است", + "description": "Title on the unlock screen" + }, + "unlock": { + "message": "باز کردن قفل", + "description": "Unlock button" + }, + "qr_transfer_title": { + "message": "انتقال این حساب", + "description": "QR sheet caption title" + }, + "qr_transfer_desc": { + "message": "این کد را با OTPilot در دستگاه دیگری اسکن کنید تا این حساب منتقل شود — هرگز از دستگاه‌های شما خارج نمی‌شود.", + "description": "QR sheet caption body" + }, + "backup_on_device": { + "message": "روی این دستگاه", + "description": "Backup section: local" + }, + "backup_cloud_sync": { + "message": "همگام‌سازی ابری", + "description": "Backup section: cloud" + }, + "backup_connected": { + "message": "متصل", + "description": "Cloud provider connected status" + }, + "backup_not_connected": { + "message": "متصل نیست", + "description": "Cloud provider not-connected status" + }, + "showing_codes_for": { + "message": "نمایش کدها برای", + "description": "Smart-filter banner prefix, followed by the site domain" + }, + "other_accounts": { + "message": "سایر حساب‌ها", + "description": "Divider above non-matching accounts in the filter view" + }, + "filter_to_site": { + "message": "فیلتر برای", + "description": "Banner prefix to re-apply the site filter, followed by the domain" + }, + "onboarding_title": { + "message": "کدهایی که همراه شما هستند", + "description": "Onboarding headline" + }, + "onboarding_subtitle": { + "message": "برای هر حساب کدهای امن دو مرحله‌ای تولید کنید — رمزنگاری‌شده، آفلاین و همیشه با یک ضربه قابل کپی.", + "description": "Onboarding subtitle" + }, + "onboarding_feature_offline": { + "message": "کاملاً به‌صورت آفلاین کار می‌کند — هیچ چیز از دستگاه شما خارج نمی‌شود", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_encrypted": { + "message": "خزانه رمزنگاری‌شده با قفل کلمه عبور اختیاری", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_scan": { + "message": "یک QR Code را اسکن کنید یا یک نسخه پشتیبان موجود را ایمپورت کنید", + "description": "Onboarding feature bullet" + }, + "onboarding_get_started": { + "message": "شروع کنید", + "description": "Onboarding primary button" + }, + "onboarding_import": { + "message": "یک نسخه پشتیبان برای ایمپورت دارم", + "description": "Onboarding secondary button" + }, "theme_light": { "message": "روشن", "description": "Light theme" @@ -313,9 +429,9 @@ "message": "تیره", "description": "Dark theme" }, - "theme_simple": { - "message": "ساده", - "description": "Simple theme" + "theme_auto": { + "message": "خودکار", + "description": "Follow the system light/dark setting" }, "theme_compact": { "message": "حالت فشرده", @@ -325,14 +441,6 @@ "message": "کنتراست بالا", "description": "High Contrast theme" }, - "theme_flat": { - "message": "تخت", - "description": "Flat theme" - }, - "storage_sync_info": { - "message": "گرفتن نسخه پشتیبان خودکار از داده‌های شما توسط سرویس‌های شخص ثالث.", - "description": "3rd party backup info" - }, "browser_sync": { "message": "همگام‌سازی مرورگر", "description": "Storage location" @@ -346,7 +454,7 @@ "description": "Sign in to 3rd party storage services" }, "onedrive_business_perms": { - "message": "چرا حساب تجاری نیاز به مجوزهای امنیتی بیشتری دارد ؟" + "message": "چرا حساب‌های تجاری به مجوزهای بیشتری نیاز دارند؟" }, "log_out": { "message": "خروج از حساب", @@ -362,10 +470,6 @@ } } }, - "otp_unsupported_warn": { - "message": "شما یک یا چند حساب Steam یا Blizzard دارید. نسخه‌های پشتیبان رمزنگاری نشده از فرمت پشتیبان استانداردسازی شده استفاده نخواهند کرد.", - "description": "Warning if using account that is not supported by standard backup format." - }, "otp_backup_inform": { "message": "می‌توانید نسخه‌های پشتیبان را از سایر برنامه‌ها ایمپورت کنید.", "description": "Info text on import page" @@ -407,20 +511,17 @@ "backup": { "message": "پشتیبان گیری" }, - "backup_file_info": { - "message": "ایجاد فایل پشتیبان از داده‌های شما" - }, "password_policy_default_hint": { - "message": "رمز عبور شما شرایط امنیتی سازمان شما را برآورده نمی کند. برای کسب اطلاعات بیشتر با سرپرست خود تماس بگیرید." + "message": "کلمه عبور شما شرایط امنیتی سازمان شما را برآورده نمی‌کند. برای کسب اطلاعات بیشتر با سرپرست خود تماس بگیرید." }, "advisor": { "message": "مشاور" }, "advisor_insight_password_not_set": { - "message": "برای محافظت از داده های خود رمز عبور تعیین کنید." + "message": "برای محافظت از داده‌های خود، کلمه عبور تعیین کنید." }, "advisor_insight_auto_lock_not_set": { - "message": "برای محافظت از داده های خود ، قفل خودکار را فعال کنید." + "message": "برای محافظت از داده‌های خود، قفل خودکار را فعال کنید." }, "advisor_insight_browser_sync_not_enabled": { "message": "همگام سازی مرورگر غیرفعال شده است. فعال کردن آن اجازه می دهد تا حساب ها در سراسر مرورگرها همگام سازی شوند." @@ -432,10 +533,10 @@ "message": "فعال کردن فیلتر هوشمند اجازه دسترسی سریع به حساب ها را می دهد." }, "show_all_insights": { - "message": "نمایش همه اطلاعات آماری." + "message": "نمایش همه بینش‌ها." }, "no_insight_available": { - "message": "بینشی یافت نشد ، همه چیز خوب به نظر می رسد!" + "message": "بینشی یافت نشد، همه چیز خوب به نظر می‌رسد!" }, "danger": { "message": "خطر" @@ -459,13 +560,13 @@ "message": "هیچ حسابی برای نمایش وجود ندارد. اکنون اولین حساب خود را اضافه کنید." }, "permissions": { - "message": "دسترسي ها" + "message": "دسترسی‌ها" }, "permission_revoke": { "message": "ابطال" }, "permission_show_required_permissions": { - "message": "نمایش دسترسی های غیر قابل لغو" + "message": "نمایش دسترسی‌های غیرقابل لغو" }, "permission_required": { "message": "این یک مجوز الزامی است و قابل ابطال نیست." @@ -489,7 +590,7 @@ "message": "وقتی روی حساب کلیک می‌کنید، به کلیپ‌بورد دسترسی فقط نوشتنی برای کپی کردن کدها در کلیپ‌بورد می‌دهد." }, "permission_context_menus": { - "message": "Authenticator را به منوی زمینه اضافه می کند." + "message": "OTPilot را به منوی زمینه اضافه می کند." }, "permission_sync_clock": { "message": "اجازه همگام سازی ساعت با Google را می دهد." @@ -500,25 +601,17 @@ "permission_dropbox_cannot_revoke": { "message": "ابتدا باید پشتیبان گیری Dropbox را غیرفعال کنید." }, - "permission_drive": { - "message": "امکان تهیه نسخه پشتیبان در Google Drive." - }, - "permission_drive_cannot_revoke": { - "message": "ابتدا باید پشتیبان‌گیری Google Drive را غیرفعال کنید." - }, - "permission_onedrive": { - "message": "امکان تهیه نسخه پشتیبان در OneDrive." - }, - "permission_onedrive_cannot_revoke": { - "message": "ابتدا باید پشتیبان گیری OneDrive را غیرفعال کنید." - }, "permission_unknown_permission": { "message": "مجوز نامعلوم. در صورت مشاهده این پیام، لطفاً یک گزارش اشکال ارسال کنید." }, "phrase_wrong": { - "message": "Password incorrect" + "message": "کلمه عبور اشتباه است." }, "activate_auto_filter": { - "message": "Warning: Smart filter loosely matches the domain name to an account. Always verify that you are on the correct website before entering a code!" + "message": "هشدار: فیلتر هوشمند نام دامنه را به‌طور تقریبی با یک حساب مطابقت می‌دهد. همیشه پیش از وارد کردن کد، مطمئن شوید که در وب‌سایت درست هستید!" + }, + "backup_unavailable": { + "message": "هنوز در دسترس نیست", + "description": "Shown on a cloud provider that is not implemented yet." } } diff --git a/_locales/fi/messages.json b/_locales/fi/messages.json index 26458d6e0..0424b4142 100644 --- a/_locales/fi/messages.json +++ b/_locales/fi/messages.json @@ -1,14 +1,22 @@ { "extName": { - "message": "Authenticator", + "message": "OTPilot Authenticator", "description": "Extension Name." }, "extShortName": { - "message": "Authenticator", + "message": "OTPilot", "description": "Extension Short Name." }, + "host": { + "message": "Sivusto", + "description": "Bound website host used to restrict autofill to a matching page." + }, + "backup_requires_password": { + "message": "Aseta ensin salasana käyttääksesi pilvivarmuuskopiointia, jotta salaisuutesi salataan ennen kuin ne lähtevät tältä laitteelta.", + "description": "Shown in the cloud backup section when no master password is set." + }, "extDesc": { - "message": "Authenticator-laajennus tuottaa kaksivaiheisen todennuksen tunnuslukuja selaimessasi.", + "message": "OTPilot Authenticator tuottaa kaksivaiheisen todennuksen (2FA) tunnuslukuja ja tarjoaa salatun pilvivarmuuskopioinnin, sivustokohtaisen automaattitäytön sekä suojausvinkkejä.", "description": "Extension Description." }, "added": { @@ -23,6 +31,10 @@ "message": "Virheellinen tilin salaisuus", "description": "Secret Error." }, + "errorissuer": { + "message": "Myöntäjä ei voi sisältää merkkejä \"::\"", + "description": "Issuer Error." + }, "add_code": { "message": "Lisää tili", "description": "Add account." @@ -40,13 +52,9 @@ "description": "Import migration data failed." }, "migration_partly_fail": { - "message": "Joitakin tilitietojen tuonti epäonnistui.", + "message": "Joidenkin tilien tietoja ei tuotu onnistuneesti.", "description": "Some migration data is broken." }, - "close": { - "message": "Sulje", - "description": "Close." - }, "ok": { "message": "Ok", "description": "OK." @@ -99,10 +107,6 @@ "message": "Nykyinen salasana", "description": "Current Passphrase." }, - "new_phrase": { - "message": "Uusi salasana", - "description": "New Passphrase." - }, "phrase": { "message": "Salasana", "description": "Passphrase." @@ -120,19 +124,15 @@ "description": "Message that user is required to acknowledge before clearing all data." }, "delete_all": { - "message": "Palauta Authenticator" + "message": "Palauta OTPilot" }, "delete_all_warning": { - "message": "Tämä poistaa kaikki tiedot ja palauttaa Authenticatorin täysin. Poistettujen tietojen palautus ei ole mahdollista! Ennen palautusta on syytä harkita varmuuskopiointia." + "message": "Tämä poistaa kaikki tiedot ja palauttaa OTPilotin täysin. Poistettujen tietojen palautus ei ole mahdollista! Ennen palautusta on syytä harkita varmuuskopiointia." }, "security_warning": { "message": "Salasanaa käytetään tiliesi salaukseen. Kukaan ei voi auttaa, jos unohdat sen.", "description": "Passphrase Warning." }, - "update": { - "message": "Päivitä", - "description": "Update." - }, "phrase_incorrect": { "message": "Et voi lisätä uutta tiliä ennen kaikkien tilien salauksen purkua. Syötä oikea salasana ennen jatkamista.", "description": "Passphrase Incorrect." @@ -157,10 +157,6 @@ "message": "Palaute", "description": "Feedback." }, - "translate": { - "message": "Käännä", - "description": "Translate." - }, "source": { "message": "Lähdekoodi", "description": "Source Code." @@ -173,10 +169,6 @@ "message": "Synkronoi kello Googlen kanssa", "description": "Sync Clock" }, - "remember_phrase": { - "message": "Muista salasana", - "description": "Remember Passphrase" - }, "clock_too_far_off": { "message": "Varoitus! Aseta kellosi oikeaan aikaan ennen jatkamista.", "description": "Local Time is Too Far Off" @@ -186,7 +178,7 @@ "description": "Remind Backup" }, "capture_failed": { - "message": "Kaappaus epäonnistu. Päivitä sivu ja yritä uudelleen.", + "message": "Kaappaus epäonnistui. Päivitä sivu ja yritä uudelleen.", "description": "Capture Failed" }, "capture_local_file_failed": { @@ -230,17 +222,49 @@ "description": "Import qr image backup." }, "import_qr_images": { - "message": "Import QR Images", + "message": "Tuo QR-koodikuvia", "description": "Import qr images. Shown as add account method." }, "import_backup_code": { - "message": "Tuo tekstivarmuuskopio", + "message": "Tuo OTPAuth-URI", "description": "Import backup code." }, "import_otp_urls": { - "message": "Import OTP URLs", + "message": "Tuo OTPAuth-URI", "description": "Import OTP URLs. Shown as add account method." }, + "import_drop_file": { + "message": "Pudota varmuuskopiotiedosto tähän tai valitse selaamalla", + "description": "File import dropzone title" + }, + "import_file_accepts": { + "message": "Hyväksyy .json- ja .txt-vientitiedostot", + "description": "File import dropzone hint" + }, + "import_choose_file": { + "message": "Valitse tiedosto", + "description": "File import dropzone button" + }, + "import_encrypted_note": { + "message": "Jos tiedosto on salattu, sinulta kysytään sen salasanaa valinnan jälkeen.", + "description": "File import note about encrypted backups" + }, + "import_enc_required": { + "message": "Salattu — salasana vaaditaan", + "description": "Badge on a picked encrypted backup file" + }, + "import_pass_placeholder": { + "message": "Syötä tiedoston salasana", + "description": "Passphrase input placeholder for encrypted import" + }, + "import_pass_hint": { + "message": "Tämä on salasana, jonka asetit varmuuskopiota luodessasi.", + "description": "Hint under the import passphrase field" + }, + "import_decrypt": { + "message": "Pura salaus ja tuo", + "description": "Button to decrypt and import an encrypted backup" + }, "import_backup_qr_partly_failed": { "message": "Tuonti onnistui, mutta joitakin QR-koodeja ei tunnistettu.", "description": "Import successful, but some QR image cannot be recognized." @@ -297,14 +321,106 @@ "message": "Käytä automaattitäyttöä", "description": "Use Autofill" }, - "use_high_contrast": { - "message": "Suuri kontrasti", - "description": "Use High Contrast" - }, "theme": { "message": "Teema", "description": "Theme" }, + "settings_appearance": { + "message": "Ulkoasu", + "description": "Settings section: appearance" + }, + "settings_general": { + "message": "Yleiset", + "description": "Settings section: general" + }, + "edit_accounts": { + "message": "Muokkaa tilejä", + "description": "Header title while in edit mode" + }, + "show_qr": { + "message": "Näytä QR-koodi", + "description": "Context-menu action to show an account's QR code" + }, + "pin_to_top": { + "message": "Kiinnitä ylimmäksi", + "description": "Context-menu action to pin an account to the top" + }, + "unpin": { + "message": "Poista kiinnitys", + "description": "Context-menu action to unpin an account" + }, + "vault_locked": { + "message": "Holvi lukittu", + "description": "Title on the unlock screen" + }, + "unlock": { + "message": "Avaa lukitus", + "description": "Unlock button" + }, + "qr_transfer_title": { + "message": "Siirrä tämä tili", + "description": "QR sheet caption title" + }, + "qr_transfer_desc": { + "message": "Lue tämä koodi OTPilotilla toisella laitteella siirtääksesi tilin — se ei koskaan poistu laitteiltasi.", + "description": "QR sheet caption body" + }, + "backup_on_device": { + "message": "Tällä laitteella", + "description": "Backup section: local" + }, + "backup_cloud_sync": { + "message": "Pilvisynkronointi", + "description": "Backup section: cloud" + }, + "backup_connected": { + "message": "Yhdistetty", + "description": "Cloud provider connected status" + }, + "backup_not_connected": { + "message": "Ei yhdistetty", + "description": "Cloud provider not-connected status" + }, + "showing_codes_for": { + "message": "Näytetään koodit sivustolle", + "description": "Smart-filter banner prefix, followed by the site domain" + }, + "other_accounts": { + "message": "Muut tilit", + "description": "Divider above non-matching accounts in the filter view" + }, + "filter_to_site": { + "message": "Suodata sivustoon", + "description": "Banner prefix to re-apply the site filter, followed by the domain" + }, + "onboarding_title": { + "message": "Koodit, jotka kulkevat mukanasi", + "description": "Onboarding headline" + }, + "onboarding_subtitle": { + "message": "Luo turvalliset kaksivaiheisen todennuksen koodit jokaiselle tilille — salattuna, offline-tilassa ja aina yhden napautuksen päässä kopioinnista.", + "description": "Onboarding subtitle" + }, + "onboarding_feature_offline": { + "message": "Toimii täysin offline-tilassa — mikään ei poistu laitteeltasi", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_encrypted": { + "message": "Salattu holvi valinnaisella salasanalukolla", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_scan": { + "message": "Lue QR-koodi tai tuo olemassa oleva varmuuskopio", + "description": "Onboarding feature bullet" + }, + "onboarding_get_started": { + "message": "Aloita", + "description": "Onboarding primary button" + }, + "onboarding_import": { + "message": "Minulla on varmuuskopio tuotavana", + "description": "Onboarding secondary button" + }, "theme_light": { "message": "Vaalea", "description": "Light theme" @@ -313,9 +429,9 @@ "message": "Tumma", "description": "Dark theme" }, - "theme_simple": { - "message": "Yksinkertainen", - "description": "Simple theme" + "theme_auto": { + "message": "Automaattinen", + "description": "Follow the system light/dark setting" }, "theme_compact": { "message": "Tiivis", @@ -325,14 +441,6 @@ "message": "Suuri kontrasti", "description": "High Contrast theme" }, - "theme_flat": { - "message": "Tasainen", - "description": "Flat theme" - }, - "storage_sync_info": { - "message": "Varmuuskopioi tietosi ulkoisiin tallennuspalveluihin automaattisesti.", - "description": "3rd party backup info" - }, "browser_sync": { "message": "Selaimen synkronointi", "description": "Storage location" @@ -362,10 +470,6 @@ } } }, - "otp_unsupported_warn": { - "message": "Sinulla on ainakin yksi Steam- tai Blizzard-tili. Salaamattomat varmuuskopiot eivät ole normaalissa varmuuskopiomuodossa.", - "description": "Warning if using account that is not supported by standard backup format." - }, "otp_backup_inform": { "message": "Voit tuoda myös joidenkin muiden sovellusten varmuuskopioita.", "description": "Info text on import page" @@ -387,7 +491,7 @@ "message": "Lisäasetukset" }, "period": { - "message": "Piste" + "message": "Jakso" }, "type": { "message": "Tyyppi" @@ -407,14 +511,11 @@ "backup": { "message": "Varmuuskopiointi" }, - "backup_file_info": { - "message": "Varmuuskopioi tietosi tiedostoon." - }, "password_policy_default_hint": { "message": "Salasanasi ei vastaa organisaatiosi suojausvaatimuksia. Lisätietoja saat järjestelmänvalvojaltasi." }, "advisor": { - "message": "Opas" + "message": "Neuvonantaja" }, "advisor_insight_password_not_set": { "message": "Aseta salasana tietojesi suojaamiseksi." @@ -438,13 +539,13 @@ "message": "Huomautettavaa ei löytynyt ja kaikki näyttää hyvältä!" }, "danger": { - "message": "Vaarallinen" + "message": "Vaara" }, "warning": { "message": "Varoitus" }, "info": { - "message": "Informatiiviinen" + "message": "Tieto" }, "dismiss": { "message": "Sulje" @@ -480,16 +581,16 @@ "message": "Sallii kirjautumisen ulkoisiin tallennuspalveluihin." }, "permission_alarms": { - "message": "Allows auto-lock to work." + "message": "Mahdollistaa automaattisen lukituksen toiminnan." }, "permission_scripting": { - "message": "Inject scripts into he current tab to scan QR codes and allow auto-fill to work." + "message": "Lisää skriptejä nykyiselle välilehdelle, jotta QR-koodien lukeminen ja automaattitäyttö toimivat." }, "permission_clipboard_write": { "message": "Myöntää pelkän kirjoitusoikeuden leikepöydälle koodien kopiointiin tilejä painettaessa." }, "permission_context_menus": { - "message": "Lisää Authenticator sisältövalikkoon." + "message": "Lisää OTPilot sisältövalikkoon." }, "permission_sync_clock": { "message": "Sallii kellon synkronoinnin Googlen kanssa." @@ -500,25 +601,17 @@ "permission_dropbox_cannot_revoke": { "message": "Dropbox-varmuuskopiointi on ensin poistettava käytöstä." }, - "permission_drive": { - "message": "Sallii varmuuskopioinnin Google Driveen." - }, - "permission_drive_cannot_revoke": { - "message": "Google Drive -varmuuskopiointi on ensin poistettava käytöstä." - }, - "permission_onedrive": { - "message": "Sallii varmuuskopioinnin OneDriveen." - }, - "permission_onedrive_cannot_revoke": { - "message": "OneDrive-varmuuskopiointi on ensin poistettava käytöstä." - }, "permission_unknown_permission": { "message": "Tuntematon käyttöoikeus. Jos näet tämän viestin, lähetä virheilmoitus." }, "phrase_wrong": { - "message": "Password incorrect" + "message": "Väärä salasana" }, "activate_auto_filter": { - "message": "Warning: Smart filter loosely matches the domain name to an account. Always verify that you are on the correct website before entering a code!" + "message": "Varoitus: Älykäs suodatin täsmää verkkotunnuksen tiliin vain löyhästi. Varmista aina, että olet oikealla sivustolla ennen kuin syötät koodin!" + }, + "backup_unavailable": { + "message": "Ei vielä saatavilla", + "description": "Shown on a cloud provider that is not implemented yet." } } diff --git a/_locales/fr/messages.json b/_locales/fr/messages.json index 05a8f0593..10bdedc4e 100644 --- a/_locales/fr/messages.json +++ b/_locales/fr/messages.json @@ -1,14 +1,22 @@ { "extName": { - "message": "Authenticator", + "message": "OTPilot Authenticator", "description": "Extension Name." }, "extShortName": { - "message": "Authenticator", + "message": "OTPilot", "description": "Extension Short Name." }, + "host": { + "message": "Site web", + "description": "Bound website host used to restrict autofill to a matching page." + }, + "backup_requires_password": { + "message": "Définissez d'abord un mot de passe pour utiliser la sauvegarde dans le cloud, afin que vos secrets soient chiffrés avant de quitter cet appareil.", + "description": "Shown in the cloud backup section when no master password is set." + }, "extDesc": { - "message": "Authenticator génère des codes d'authentification à deux facteurs dans votre navigateur.", + "message": "OTPilot Authenticator génère des codes d'authentification à deux facteurs (2FA), avec sauvegarde chiffrée dans le cloud, remplissage automatique associé au site et conseils de sécurité.", "description": "Extension Description." }, "added": { @@ -23,6 +31,10 @@ "message": "Secret du compte invalide", "description": "Secret Error." }, + "errorissuer": { + "message": "L'émetteur ne peut pas contenir \"::\"", + "description": "Issuer Error." + }, "add_code": { "message": "Ajouter un compte", "description": "Add account." @@ -43,10 +55,6 @@ "message": "Certaines données de compte n'ont pas été importées correctement.", "description": "Some migration data is broken." }, - "close": { - "message": "Fermer", - "description": "Close." - }, "ok": { "message": "Ok", "description": "OK." @@ -99,16 +107,12 @@ "message": "Mot de passe actuel", "description": "Current Passphrase." }, - "new_phrase": { - "message": "Nouveau mot de passe", - "description": "New Passphrase." - }, "phrase": { "message": "Mot de passe", "description": "Passphrase." }, "confirm_phrase": { - "message": "Confirmer le Mot de Passe", + "message": "Confirmer le mot de passe", "description": "Confirm Passphrase." }, "confirm_delete": { @@ -120,19 +124,15 @@ "description": "Message that user is required to acknowledge before clearing all data." }, "delete_all": { - "message": "Réinitialiser Authentificator" + "message": "Réinitialiser OTPilot" }, "delete_all_warning": { - "message": "Cela supprimera toutes vos données et réinitialisera complètement Authentificator. Vous ne serez pas en mesure de récupérer des données supprimées ! Vous devriez envisager d'enregistrer une sauvegarde avant de réinitialiser Authentificator." + "message": "Cela supprimera toutes vos données et réinitialisera complètement OTPilot. Vous ne serez pas en mesure de récupérer des données supprimées ! Vous devriez envisager d'enregistrer une sauvegarde avant de réinitialiser OTPilot." }, "security_warning": { - "message": "Ce mot de passe sera utilisé pour crypter vos comptes. Personne ne peut vous aider si vous oubliez le mot de passe.", + "message": "Ce mot de passe sera utilisé pour chiffrer vos comptes. Personne ne peut vous aider si vous oubliez le mot de passe.", "description": "Passphrase Warning." }, - "update": { - "message": "Mettre à jour", - "description": "Update." - }, "phrase_incorrect": { "message": "Vous ne pouvez pas ajouter de nouveau compte tant que tous les comptes ne sont pas déchiffrés. Veuillez entrer le mot de passe correct avant de continuer.", "description": "Passphrase Incorrect." @@ -142,7 +142,7 @@ "description": "Skip Unable-decripted Data." }, "phrase_not_match": { - "message": "Le mot de passe est incorrect.", + "message": "Les mots de passe ne correspondent pas.", "description": "Passphrase Not Match." }, "encrypted": { @@ -157,10 +157,6 @@ "message": "Commentaires", "description": "Feedback." }, - "translate": { - "message": "Traduire", - "description": "Translate." - }, "source": { "message": "Code source", "description": "Source Code." @@ -173,10 +169,6 @@ "message": "Synchroniser l'horloge avec celle de Google", "description": "Sync Clock" }, - "remember_phrase": { - "message": "Enregistrer le mot de passe", - "description": "Remember Passphrase" - }, "clock_too_far_off": { "message": "Attention ! Un décalage trop important existe avec votre horloge locale, réglez ce problème avant de continuer.", "description": "Local Time is Too Far Off" @@ -234,13 +226,45 @@ "description": "Import qr images. Shown as add account method." }, "import_backup_code": { - "message": "Importation au format texte", + "message": "Importer une URI OTPAuth", "description": "Import backup code." }, "import_otp_urls": { "message": "Importer des URL OTP", "description": "Import OTP URLs. Shown as add account method." }, + "import_drop_file": { + "message": "Déposez un fichier de sauvegarde ou cliquez pour parcourir", + "description": "File import dropzone title" + }, + "import_file_accepts": { + "message": "Accepte les exports .json et .txt", + "description": "File import dropzone hint" + }, + "import_choose_file": { + "message": "Choisir un fichier", + "description": "File import dropzone button" + }, + "import_encrypted_note": { + "message": "Si le fichier est chiffré, son mot de passe vous sera demandé après l'avoir sélectionné.", + "description": "File import note about encrypted backups" + }, + "import_enc_required": { + "message": "Chiffré — mot de passe requis", + "description": "Badge on a picked encrypted backup file" + }, + "import_pass_placeholder": { + "message": "Saisissez le mot de passe du fichier", + "description": "Passphrase input placeholder for encrypted import" + }, + "import_pass_hint": { + "message": "Il s'agit du mot de passe que vous avez défini lors de la création de la sauvegarde.", + "description": "Hint under the import passphrase field" + }, + "import_decrypt": { + "message": "Déchiffrer et importer", + "description": "Button to decrypt and import an encrypted backup" + }, "import_backup_qr_partly_failed": { "message": "Importation réussie, mais certains codes QR n'ont pas pu être reconnus.", "description": "Import successful, but some QR image cannot be recognized." @@ -258,7 +282,7 @@ "description": "Backup risk warning." }, "import_error_password": { - "message": "Vous devez fournir le mot de passe pour importer une sauvegarde.", + "message": "Vous devez fournir le mot de passe correct pour importer une sauvegarde.", "description": "Error password warning when import backups." }, "local_passphrase_warning": { @@ -297,14 +321,106 @@ "message": "Utiliser le remplissage automatique", "description": "Use Autofill" }, - "use_high_contrast": { - "message": "Utiliser le contraste élevé", - "description": "Use High Contrast" - }, "theme": { "message": "Thème", "description": "Theme" }, + "settings_appearance": { + "message": "Apparence", + "description": "Settings section: appearance" + }, + "settings_general": { + "message": "Général", + "description": "Settings section: general" + }, + "edit_accounts": { + "message": "Modifier les comptes", + "description": "Header title while in edit mode" + }, + "show_qr": { + "message": "Afficher le code QR", + "description": "Context-menu action to show an account's QR code" + }, + "pin_to_top": { + "message": "Épingler en haut", + "description": "Context-menu action to pin an account to the top" + }, + "unpin": { + "message": "Détacher", + "description": "Context-menu action to unpin an account" + }, + "vault_locked": { + "message": "Coffre verrouillé", + "description": "Title on the unlock screen" + }, + "unlock": { + "message": "Déverrouiller", + "description": "Unlock button" + }, + "qr_transfer_title": { + "message": "Transférer ce compte", + "description": "QR sheet caption title" + }, + "qr_transfer_desc": { + "message": "Scannez ce code avec OTPilot sur un autre appareil pour transférer ce compte — il ne quitte jamais vos appareils.", + "description": "QR sheet caption body" + }, + "backup_on_device": { + "message": "Sur cet appareil", + "description": "Backup section: local" + }, + "backup_cloud_sync": { + "message": "Synchronisation cloud", + "description": "Backup section: cloud" + }, + "backup_connected": { + "message": "Connecté", + "description": "Cloud provider connected status" + }, + "backup_not_connected": { + "message": "Non connecté", + "description": "Cloud provider not-connected status" + }, + "showing_codes_for": { + "message": "Affichage des codes pour", + "description": "Smart-filter banner prefix, followed by the site domain" + }, + "other_accounts": { + "message": "Autres comptes", + "description": "Divider above non-matching accounts in the filter view" + }, + "filter_to_site": { + "message": "Filtrer sur", + "description": "Banner prefix to re-apply the site filter, followed by the domain" + }, + "onboarding_title": { + "message": "Des codes qui vous suivent partout", + "description": "Onboarding headline" + }, + "onboarding_subtitle": { + "message": "Générez des codes à deux facteurs sécurisés pour chaque compte — chiffrés, hors ligne, et toujours à un clic de la copie.", + "description": "Onboarding subtitle" + }, + "onboarding_feature_offline": { + "message": "Fonctionne entièrement hors ligne — rien ne quitte votre appareil", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_encrypted": { + "message": "Coffre chiffré avec verrouillage par mot de passe facultatif", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_scan": { + "message": "Scannez un QR code ou importez une sauvegarde existante", + "description": "Onboarding feature bullet" + }, + "onboarding_get_started": { + "message": "Commencer", + "description": "Onboarding primary button" + }, + "onboarding_import": { + "message": "J'ai une sauvegarde à importer", + "description": "Onboarding secondary button" + }, "theme_light": { "message": "Clair", "description": "Light theme" @@ -313,9 +429,9 @@ "message": "Sombre", "description": "Dark theme" }, - "theme_simple": { - "message": "Simple", - "description": "Simple theme" + "theme_auto": { + "message": "Automatique", + "description": "Follow the system light/dark setting" }, "theme_compact": { "message": "Compact", @@ -325,14 +441,6 @@ "message": "Contraste élevé", "description": "High Contrast theme" }, - "theme_flat": { - "message": "Plat", - "description": "Flat theme" - }, - "storage_sync_info": { - "message": "Sauvegardez automatiquement vos données vers des services de stockage tiers.", - "description": "3rd party backup info" - }, "browser_sync": { "message": "Synchronisation du navigateur", "description": "Storage location" @@ -353,7 +461,7 @@ "description": "Sign out of 3rd party storage services" }, "token_revoked": { - "message": "Il y a eu un problème de connexion à votre compte $SERVICE$ , veuillez vous connecter à nouveau.", + "message": "Il y a eu un problème de connexion à votre compte $SERVICE$, veuillez vous connecter à nouveau.", "description": "Error authenticating to backup service. $SERVICE$ will be replaced with a proper noun (E.g.: 'Google Drive' or 'Dropbox')", "placeholders": { "service": { @@ -362,10 +470,6 @@ } } }, - "otp_unsupported_warn": { - "message": "Vous avez un ou plusieurs comptes Steam ou Blizzard. Les sauvegardes non chiffrées n'utiliseront pas de format de sauvegarde standardisé.", - "description": "Warning if using account that is not supported by standard backup format." - }, "otp_backup_inform": { "message": "Vous pouvez importer des sauvegardes depuis d'autres applications.", "description": "Info text on import page" @@ -402,14 +506,11 @@ "message": "Algorithme" }, "smart_filter": { - "message": "Filtre Intelligent" + "message": "Filtre intelligent" }, "backup": { "message": "Sauvegarde" }, - "backup_file_info": { - "message": "Sauvegardez vos données dans un fichier." - }, "password_policy_default_hint": { "message": "Votre mot de passe ne répond pas aux exigences de sécurité de votre organisation. Contactez votre administrateur pour plus d'informations." }, @@ -423,7 +524,7 @@ "message": "Activer le verrouillage automatique pour protéger vos données." }, "advisor_insight_browser_sync_not_enabled": { - "message": "La synchronisation avec le navigateur est désactivée. Activez-la pour autoriser cette synchronisation." + "message": "La synchronisation du navigateur est désactivée. L'activer permet de synchroniser les comptes entre plusieurs navigateurs." }, "advisor_insight_auto_fill_not_enabled": { "message": "Le remplissage automatique peut être activé pour remplir automatiquement les codes dans les sites web." @@ -432,10 +533,10 @@ "message": "Activer le filtre intelligent permet un accès rapide aux comptes." }, "show_all_insights": { - "message": "Afficher tous les aperçus." + "message": "Afficher toutes les recommandations." }, "no_insight_available": { - "message": "Aucun aperçu trouvé, tout semble bien!" + "message": "Aucune recommandation, tout est en ordre !" }, "danger": { "message": "Danger" @@ -447,7 +548,7 @@ "message": "Information" }, "dismiss": { - "message": "Fermer" + "message": "Ignorer" }, "learn_more": { "message": "En savoir plus" @@ -468,7 +569,7 @@ "message": "Afficher les permissions non révocables" }, "permission_required": { - "message": "Il s'agit d'une autorisation nécessaire et ne peut pas être révoquée." + "message": "Cette permission est obligatoire et ne peut pas être révoquée." }, "permission_active_tab": { "message": "Accéder à l'onglet actuel pour scanner le code QR." @@ -477,7 +578,7 @@ "message": "Accès au stockage du navigateur pour y stocker les données du compte." }, "permission_identity": { - "message": "Permet de se connecter à des services de stockage tiers (ex: Google Drive, Dropbox...)." + "message": "Permet de se connecter à des services de stockage tiers." }, "permission_alarms": { "message": "Permet au verrouillage automatique de fonctionner." @@ -489,29 +590,17 @@ "message": "Donne un accès en écriture seule au presse-papiers pour copier des codes dans le presse-papiers lorsque vous cliquez sur le compte." }, "permission_context_menus": { - "message": "Ajoute Authenticator au menu contextuel." + "message": "Ajoute OTPilot au menu contextuel." }, "permission_sync_clock": { - "message": "Autoriser la synchronisation de l'horloge avec Google." + "message": "Permet la synchronisation de l'horloge avec Google." }, "permission_dropbox": { - "message": "Autoriser la sauvegarde vers Dropbox." + "message": "Permet la sauvegarde vers Dropbox." }, "permission_dropbox_cannot_revoke": { "message": "Vous devez d'abord désactiver la sauvegarde Dropbox." }, - "permission_drive": { - "message": "Autoriser la sauvegarde vers Google Drive." - }, - "permission_drive_cannot_revoke": { - "message": "Vous devez d'abord désactiver la sauvegarde de Google Drive." - }, - "permission_onedrive": { - "message": "Autoriser la sauvegarde vers OneDrive." - }, - "permission_onedrive_cannot_revoke": { - "message": "Vous devez d'abord désactiver la sauvegarde OneDrive." - }, "permission_unknown_permission": { "message": "Autorisation inconnue. Si vous voyez ce message, veuillez envoyer un rapport de bug." }, @@ -519,6 +608,10 @@ "message": "Mot de passe incorrect" }, "activate_auto_filter": { - "message": "Avertissement: Le filtre intelligent associe grosso modo le nom de domaine à un compte. Vérifiez toujours que vous êtes sur le bon site avant d'entrer un code !" + "message": "Attention : le filtre intelligent associe grosso modo le nom de domaine à un compte. Vérifiez toujours que vous êtes sur le bon site avant d'entrer un code !" + }, + "backup_unavailable": { + "message": "Pas encore disponible", + "description": "Shown on a cloud provider that is not implemented yet." } } diff --git a/_locales/fy/messages.json b/_locales/fy/messages.json index aff586db0..2f9c79243 100644 --- a/_locales/fy/messages.json +++ b/_locales/fy/messages.json @@ -1,14 +1,22 @@ { "extName": { - "message": "Authenticator", + "message": "OTPilot Authenticator", "description": "Extension Name." }, "extShortName": { - "message": "Authenticator", + "message": "OTPilot", "description": "Extension Short Name." }, + "host": { + "message": "Webside", + "description": "Bound website host used to restrict autofill to a matching page." + }, + "backup_requires_password": { + "message": "Stel earst in wachtwurd yn om cloud-reservekopy te brûken, sadat jo geheimen fersifere binne foardat se dit apparaat ferlitte.", + "description": "Shown in the cloud backup section when no master password is set." + }, "extDesc": { - "message": "Authenticator generearret 2-stapsferifikaasjekoades yn jo browser.", + "message": "OTPilot Authenticator generearret twafaktor (2FA) koades, mei fersifere cloud-reservekopy, host-keppele automatysk ynfoljen, en befeiligingsadvys.", "description": "Extension Description." }, "added": { @@ -20,9 +28,13 @@ "description": "QR Error." }, "errorsecret": { - "message": "Net krekt accountgeheim", + "message": "Ûnjildich accountgeheim", "description": "Secret Error." }, + "errorissuer": { + "message": "Útjouwer mei net \"::\" befetsje", + "description": "Issuer Error." + }, "add_code": { "message": "Account tafoegje", "description": "Add account." @@ -43,10 +55,6 @@ "message": "Guon accountgegevens binne net ymportearre.", "description": "Some migration data is broken." }, - "close": { - "message": "Slute", - "description": "Close." - }, "ok": { "message": "OK", "description": "OK." @@ -68,7 +76,7 @@ "description": "Account Name." }, "issuer": { - "message": "Utjouwer", + "message": "Útjouwer", "description": "Issuer." }, "secret": { @@ -96,13 +104,9 @@ "description": "Security." }, "current_phrase": { - "message": "Aktueel ​​wachtwurd", + "message": "Hjoeddeistich wachtwurd", "description": "Current Passphrase." }, - "new_phrase": { - "message": "Nij wachtwurd", - "description": "New Passphrase." - }, "phrase": { "message": "Wachtwurd", "description": "Passphrase." @@ -116,23 +120,19 @@ "description": "Remove entry confirmation" }, "confirm_delete_all": { - "message": "Ik begryp dat al myn gegevens sûnder te werstellen fuortsmiten wurde sille.", + "message": "Ik begryp dat al myn gegevens ûnwerstelber fuortsmiten wurde sille.", "description": "Message that user is required to acknowledge before clearing all data." }, "delete_all": { - "message": "Authenticator opnij inisjalisearje" + "message": "OTPilot opnij inisjalisearje" }, "delete_all_warning": { - "message": "Dit sil al jo gegevens fuortsmite en de Authenticator folslein opnij inisjalisearje. Jo binne net yn steat om fuortsmiten gegevens te werstellen! Oerwaagje om in reservekopy te bewarjen eardat jo de Authenticator opnij inisjalisearje." + "message": "Dit sil al jo gegevens fuortsmite en OTPilot folslein opnij inisjalisearje. Jo binne net yn steat om fuortsmiten gegevens te werstellen! Oerwaagje om in reservekopy te bewarjen eardat jo OTPilot opnij inisjalisearje." }, "security_warning": { "message": "Dit wachtwurd wurdt brûkt foar it fersiferjen fan jo accounts. Net ien kin jo helpe as jo it wachtwurd ferjitte.", "description": "Passphrase Warning." }, - "update": { - "message": "Fernije", - "description": "Update." - }, "phrase_incorrect": { "message": "Jo kinne net in nij account tafoegje oant alle accounts ûntsifere binne. Fier it krekte wachtwurd yn eardat jo fierder gean.", "description": "Passphrase Incorrect." @@ -157,10 +157,6 @@ "message": "Kommentaar", "description": "Feedback." }, - "translate": { - "message": "Oersette", - "description": "Translate." - }, "source": { "message": "Boarnekoade", "description": "Source Code." @@ -173,10 +169,6 @@ "message": "Klok syngronisearje mei Google", "description": "Sync Clock" }, - "remember_phrase": { - "message": "Wachtwurd ûnthâlde", - "description": "Remember Passphrase" - }, "clock_too_far_off": { "message": "Let op! De klok fan jo lokale kompjûter is te fier foar/efter, werstel dit eardat jo trochgean.", "description": "Local Time is Too Far Off" @@ -198,7 +190,7 @@ "description": "Time Based" }, "based_on_counter": { - "message": "Teller bûn", + "message": "Tellerbûn", "description": "Counter Based" }, "resize_popup_page": { @@ -206,7 +198,7 @@ "description": "Popup Page Settings" }, "scale": { - "message": "Skeal", + "message": "Skaal", "description": "Scale" }, "export_info": { @@ -234,13 +226,45 @@ "description": "Import qr images. Shown as add account method." }, "import_backup_code": { - "message": "Tekstreservekopy ymportearje", + "message": "OTP-URL's ymportearje", "description": "Import backup code." }, "import_otp_urls": { "message": "OTP-URL’s ymportearje", "description": "Import OTP URLs. Shown as add account method." }, + "import_drop_file": { + "message": "Sleep in reservekopybestân hjirhinne, of klik om te blêdzjen", + "description": "File import dropzone title" + }, + "import_file_accepts": { + "message": "Akseptearret .json- en .txt-eksporten", + "description": "File import dropzone hint" + }, + "import_choose_file": { + "message": "Bestân kieze", + "description": "File import dropzone button" + }, + "import_encrypted_note": { + "message": "As it bestân fersifere is, wurde jo nei seleksje om it wachtwurd frege.", + "description": "File import note about encrypted backups" + }, + "import_enc_required": { + "message": "Fersifere — wachtwurd fereaske", + "description": "Badge on a picked encrypted backup file" + }, + "import_pass_placeholder": { + "message": "Fier it wachtwurd fan it bestân yn", + "description": "Passphrase input placeholder for encrypted import" + }, + "import_pass_hint": { + "message": "Dit is it wachtwurd dat jo ynsteld hawwe doe't jo de reservekopy makken.", + "description": "Hint under the import passphrase field" + }, + "import_decrypt": { + "message": "Ûntsiferje en ymportearje", + "description": "Button to decrypt and import an encrypted backup" + }, "import_backup_qr_partly_failed": { "message": "Ymport suksesfol, mar guon QR-koaden kinne net werkend wurde.", "description": "Import successful, but some QR image cannot be recognized." @@ -250,7 +274,7 @@ "description": "You can select multiple image files to import backup in batches." }, "show_all_entries": { - "message": "Lit alle ynfier sjen", + "message": "Lit alle ynfieren sjen", "description": "Show all entries." }, "dropbox_risk": { @@ -262,7 +286,7 @@ "description": "Error password warning when import backups." }, "local_passphrase_warning": { - "message": "Jo wachtwurd is lokaal bewarre, wizigje dizze daliks yn it menu befeiliging.", + "message": "Jo wachtwurd is lokaal bewarre, wizigje it daliks yn it menu befeiliging.", "description": "localStorage password warning." }, "remove": { @@ -294,17 +318,109 @@ "description": "Manual backup" }, "use_autofill": { - "message": "Automatysk oanfolje brûke", + "message": "Automatysk ynfolje brûke", "description": "Use Autofill" }, - "use_high_contrast": { - "message": "Heech kontrast brûke", - "description": "Use High Contrast" - }, "theme": { "message": "Tema", "description": "Theme" }, + "settings_appearance": { + "message": "Uterlik", + "description": "Settings section: appearance" + }, + "settings_general": { + "message": "Algemien", + "description": "Settings section: general" + }, + "edit_accounts": { + "message": "Accounts bewurkje", + "description": "Header title while in edit mode" + }, + "show_qr": { + "message": "QR-koade toane", + "description": "Context-menu action to show an account's QR code" + }, + "pin_to_top": { + "message": "Boppeoan fêstsette", + "description": "Context-menu action to pin an account to the top" + }, + "unpin": { + "message": "Losmeitsje", + "description": "Context-menu action to unpin an account" + }, + "vault_locked": { + "message": "Kluis beskoattele", + "description": "Title on the unlock screen" + }, + "unlock": { + "message": "Ûntskoattelje", + "description": "Unlock button" + }, + "qr_transfer_title": { + "message": "Dit account oerdrage", + "description": "QR sheet caption title" + }, + "qr_transfer_desc": { + "message": "Scan dizze koade mei OTPilot op in oar apparaat om dit account oer te setten — it ferlit noait jo apparaten.", + "description": "QR sheet caption body" + }, + "backup_on_device": { + "message": "Op dit apparaat", + "description": "Backup section: local" + }, + "backup_cloud_sync": { + "message": "Cloudsyngronisaasje", + "description": "Backup section: cloud" + }, + "backup_connected": { + "message": "Ferbûn", + "description": "Cloud provider connected status" + }, + "backup_not_connected": { + "message": "Net ferbûn", + "description": "Cloud provider not-connected status" + }, + "showing_codes_for": { + "message": "Koaden toand foar", + "description": "Smart-filter banner prefix, followed by the site domain" + }, + "other_accounts": { + "message": "Oare accounts", + "description": "Divider above non-matching accounts in the filter view" + }, + "filter_to_site": { + "message": "Filterje op", + "description": "Banner prefix to re-apply the site filter, followed by the domain" + }, + "onboarding_title": { + "message": "Koaden dy't mei jo reizgje", + "description": "Onboarding headline" + }, + "onboarding_subtitle": { + "message": "Generearje feilige twastapskoaden foar elk account — fersifere, offline, en altyd mar ien tik fan kopiearjen.", + "description": "Onboarding subtitle" + }, + "onboarding_feature_offline": { + "message": "Wurket folslein offline — neat ferlit jo apparaat", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_encrypted": { + "message": "Fersifere kluis mei opsjonele wachtwurdbeskoatteling", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_scan": { + "message": "Scan in QR-koade of ymportearje in besteande reservekopy", + "description": "Onboarding feature bullet" + }, + "onboarding_get_started": { + "message": "Begjinne", + "description": "Onboarding primary button" + }, + "onboarding_import": { + "message": "Ik haw in reservekopy om te ymportearjen", + "description": "Onboarding secondary button" + }, "theme_light": { "message": "Ljocht", "description": "Light theme" @@ -313,9 +429,9 @@ "message": "Donker", "description": "Dark theme" }, - "theme_simple": { - "message": "Ienfâldich", - "description": "Simple theme" + "theme_auto": { + "message": "Auto", + "description": "Follow the system light/dark setting" }, "theme_compact": { "message": "Kompakt", @@ -325,14 +441,6 @@ "message": "Heech kontrast", "description": "High Contrast theme" }, - "theme_flat": { - "message": "Flak", - "description": "Flat theme" - }, - "storage_sync_info": { - "message": "Automatysk in reservekopy meitsje fan jo gegevens nei opslachtsjinsten fan tredden.", - "description": "3rd party backup info" - }, "browser_sync": { "message": "Browsersyngronisaasje", "description": "Storage location" @@ -346,10 +454,10 @@ "description": "Sign in to 3rd party storage services" }, "onedrive_business_perms": { - "message": "Wêrom hawwe saaklike accounts mear tastimming nedich?" + "message": "Wêrom hawwe saaklike accounts mear tastimmingen nedich?" }, "log_out": { - "message": "Ofmelde", + "message": "Ôfmelde", "description": "Sign out of 3rd party storage services" }, "token_revoked": { @@ -362,12 +470,8 @@ } } }, - "otp_unsupported_warn": { - "message": "Jo hawwe ien of mear Steam- of Blizzard-accounts. Net-fersifere reservekopyen sille gjin standerdisearre reservekopy-yndieling brûke.", - "description": "Warning if using account that is not supported by standard backup format." - }, "otp_backup_inform": { - "message": "Jo kinne reservekopyen fan inkelde andere tapassingen ymportearje.", + "message": "Jo kinne reservekopyen fan guon oare tapassingen ymportearje.", "description": "Info text on import page" }, "otp_backup_learn": { @@ -393,10 +497,10 @@ "message": "Type" }, "invalid": { - "message": "Unjildich" + "message": "Ûnjildich" }, "digits": { - "message": "Digits" + "message": "Sifers" }, "algorithm": { "message": "Algoritme" @@ -407,9 +511,6 @@ "backup": { "message": "Reservekopy" }, - "backup_file_info": { - "message": "Meitsje in reservekopy fan jo gegevens nei in bestân." - }, "password_policy_default_hint": { "message": "Jo wachtwurd foldocht net oan de befeiligingseasken fan jo organisaasje. Nim kontakt op mei jo behearder foar mear ynformaasje." }, @@ -468,7 +569,7 @@ "message": "Net-werropbere tastimmingen werjaan" }, "permission_required": { - "message": "Dit is in fereaske tastimming dy’t net fuort te smiten is." + "message": "Dit is in fereaske tastimming dy’t net ynlutsen wurde kin." }, "permission_active_tab": { "message": "Tagong ta it aktuele ljepblêd om QR-koaden te scannen." @@ -489,7 +590,7 @@ "message": "Jout allinnich-skriuwrjochttagong ta it klamboerd om koaden nei it klamboerd te kopiearjen wannear’t jo op de account klikke." }, "permission_context_menus": { - "message": "Foeget Authenticator ta oan kontekstmenu." + "message": "Foeget OTPilot ta oan kontekstmenu." }, "permission_sync_clock": { "message": "Tiidssyngronisaasje mei Google tastean." @@ -500,25 +601,17 @@ "permission_dropbox_cannot_revoke": { "message": "Jo moatte earst reservekopy nei Dropbox útskeakelje." }, - "permission_drive": { - "message": "Reservekopy nei Google Drive tastean." - }, - "permission_drive_cannot_revoke": { - "message": "Jo moatte earst reservekopy nei Google Drive útskeakelje." - }, - "permission_onedrive": { - "message": "Reservekopy nei OneDrive tastean." - }, - "permission_onedrive_cannot_revoke": { - "message": "Jo moatte earst reservekopy nei OneDrive útskeakelje." - }, "permission_unknown_permission": { - "message": "Unbekende tastimming. As dit bericht werjûn wurdt, stjoer dan in bugrapport." + "message": "Ûnbekende tastimming. As dit berjocht werjûn wurdt, stjoer dan in bugrapport." }, "phrase_wrong": { - "message": "Wachtwurd net korrekt" + "message": "Wachtwurd net krekt" }, "activate_auto_filter": { - "message": "Warskôging: Tûk filter keppelet op in frij losse manier de domeinnamme oan in account. Kontrolearje altyd oft jo op de krekte website binne eardat jo in koade ynfiere!" + "message": "Warskôging: Tûk filter keppelet op in frij losse manier de domeinnamme oan in account. Kontrolearje altyd oft jo op de krekte webside binne eardat jo in koade ynfiere!" + }, + "backup_unavailable": { + "message": "Noch net beskikber", + "description": "Shown on a cloud provider that is not implemented yet." } } diff --git a/_locales/he/messages.json b/_locales/he/messages.json index 32dd7e5e0..40589923d 100644 --- a/_locales/he/messages.json +++ b/_locales/he/messages.json @@ -1,14 +1,22 @@ { "extName": { - "message": "מאמת", + "message": "OTPilot Authenticator", "description": "Extension Name." }, "extShortName": { - "message": "מאמת", + "message": "OTPilot", "description": "Extension Short Name." }, + "host": { + "message": "אתר", + "description": "Bound website host used to restrict autofill to a matching page." + }, + "backup_requires_password": { + "message": "כדי להשתמש בגיבוי בענן יש להגדיר סיסמה תחילה, כך שהסודות שלך יוצפנו לפני שהם יוצאים מהמכשיר הזה.", + "description": "Shown in the cloud backup section when no master password is set." + }, "extDesc": { - "message": "המאמת מחולל קודי אימות דו שלבי ישירות מהדפדפן.", + "message": "OTPilot Authenticator מייצר קודי אימות דו-שלביים (2FA), עם גיבוי מוצפן בענן, מילוי אוטומטי מותאם לאתר וייעוץ אבטחה.", "description": "Extension Description." }, "added": { @@ -23,12 +31,16 @@ "message": "קוד סודי שגוי", "description": "Secret Error." }, + "errorissuer": { + "message": "שם המנפיק אינו יכול להכיל \"::\"", + "description": "Issuer Error." + }, "add_code": { "message": "הוספת חשבון", "description": "Add account." }, "add_qr": { - "message": "סרוק ברקוד", + "message": "סריקת קוד QR", "description": "Scan QR Code." }, "add_secret": { @@ -36,17 +48,13 @@ "description": "Manual Entry." }, "migration_fail": { - "message": "היבוא נכשל. אם מדובר ביבוא מהמאמת של גוגל, יש ליצא את המידע מהיישומון של גוגל ולנסות שוב.", + "message": "הייבוא נכשל. אם אתה מעביר נתונים מ-Google Authenticator, יש לייצא את הנתונים שוב מ-Google Authenticator ולנסות שוב.", "description": "Import migration data failed." }, "migration_partly_fail": { "message": "חלק ממידע החשבון לא יובא בהצלחה.", "description": "Some migration data is broken." }, - "close": { - "message": "סגירה", - "description": "Close." - }, "ok": { "message": "אישור", "description": "OK." @@ -99,10 +107,6 @@ "message": "סיסמה נוכחית", "description": "Current Passphrase." }, - "new_phrase": { - "message": "סיסמה חדשה", - "description": "New Passphrase." - }, "phrase": { "message": "סיסמה", "description": "Passphrase." @@ -120,21 +124,17 @@ "description": "Message that user is required to acknowledge before clearing all data." }, "delete_all": { - "message": "איפוס מאמת" + "message": "איפוס OTPilot" }, "delete_all_warning": { - "message": "פעולה זו תמחוק את כל המידע שלך ותאפס את המאמת. שחזור המידע האישי לא יהיה אפשרי! מומלץ לשמור גיבוי לפני איפוס המאמת." + "message": "פעולה זו תמחק את כל הנתונים שלך ותאפס את OTPilot לחלוטין. לא תהיה אפשרות לשחזר נתונים שנמחקו! מומלץ לשמור גיבוי לפני איפוס OTPilot." }, "security_warning": { "message": "סיסמה זו תשמש להצפנת חשבונותיך. אין דרך לשחזר את הסיסמה אם היא תשכח.", "description": "Passphrase Warning." }, - "update": { - "message": "עדכון", - "description": "Update." - }, "phrase_incorrect": { - "message": "לא ניתן להוסיף חשבון חדש עד שכל החשבונות יפוענחו. נא להכניס סיסמא נכונה כדי להמשיך.", + "message": "לא ניתן להוסיף חשבון חדש עד שכל החשבונות יפוענחו. יש להזין את הסיסמה הנכונה כדי להמשיך.", "description": "Passphrase Incorrect." }, "phrase_incorrect_export": { @@ -157,32 +157,24 @@ "message": "משוב", "description": "Feedback." }, - "translate": { - "message": "תרגם", - "description": "Translate." - }, "source": { "message": "קוד מקור", "description": "Source Code." }, "passphrase_info": { - "message": "יש להזין סיסמה לצורך לפענוח נתוני חשבון.", + "message": "יש להזין סיסמה כדי לפענח את נתוני החשבון.", "description": "Passphrase Info" }, "sync_clock": { - "message": "סינכרון שעון עם גוגל", + "message": "סינכרון שעון עם Google", "description": "Sync Clock" }, - "remember_phrase": { - "message": "זכור את הסיסמה", - "description": "Remember Passphrase" - }, "clock_too_far_off": { "message": "זהירות! השעון המקומי שלך רחוק מדי, יש לתקן אותו לפני שנמשיך.", "description": "Local Time is Too Far Off" }, "remind_backup": { - "message": "האם יש לך גיבוי לחשבונות שלך? לא לחכות עד שיהיה מאוחר מדי!", + "message": "יש לך גיבוי לחשבונות שלך? אל תחכה עד שיהיה מאוחר מדי!", "description": "Remind Backup" }, "capture_failed": { @@ -190,7 +182,7 @@ "description": "Capture Failed" }, "capture_local_file_failed": { - "message": "האם ניסית לסרוק קוד QR מקובץ מקומי? יש להשתמש בתמונת גיבוי QR במקום זאת.", + "message": "מנסה לסרוק קוד QR מקובץ מקומי? השתמש באפשרות 'ייבא תמונת גיבוי QR' במקום זאת.", "description": "Import QR image backup instead of scan local image" }, "based_on_time": { @@ -210,7 +202,7 @@ "description": "Scale" }, "export_info": { - "message": "אזהרה: כל הגיבויים אינם מוצפנים. רוצה להוסיף חשבון לאפליקציה אחרת? רחף מעל החלק השמאלי העליון של חשבון כלשהו ולחץ על הכפתור המוסתר.", + "message": "אזהרה: כל הגיבויים אינם מוצפנים. רוצה להוסיף חשבון לאפליקציה אחרת? רחף מעל החלק הימני העליון של חשבון כלשהו ולחץ על הכפתור המוסתר.", "description": "Export menu info text" }, "download_backup": { @@ -226,23 +218,55 @@ "description": "Import backup file." }, "import_backup_qr": { - "message": "יבוא תמונת גיבוי QR", + "message": "ייבא תמונת גיבוי QR", "description": "Import qr image backup." }, "import_qr_images": { - "message": "Import QR Images", + "message": "ייבוא תמונות QR", "description": "Import qr images. Shown as add account method." }, "import_backup_code": { - "message": "ייבא גיבוי כטקסט", + "message": "ייבוא כתובת OTPAuth URI", "description": "Import backup code." }, "import_otp_urls": { - "message": "Import OTP URLs", + "message": "ייבוא כתובת OTPAuth URI", "description": "Import OTP URLs. Shown as add account method." }, + "import_drop_file": { + "message": "גרור קובץ גיבוי לכאן או לחץ לעיון", + "description": "File import dropzone title" + }, + "import_file_accepts": { + "message": "תומך בקובצי ייצוא .json ו-.txt", + "description": "File import dropzone hint" + }, + "import_choose_file": { + "message": "בחר קובץ", + "description": "File import dropzone button" + }, + "import_encrypted_note": { + "message": "אם הקובץ מוצפן, תתבקש להזין את הסיסמה שלו לאחר הבחירה.", + "description": "File import note about encrypted backups" + }, + "import_enc_required": { + "message": "מוצפן — נדרשת סיסמה", + "description": "Badge on a picked encrypted backup file" + }, + "import_pass_placeholder": { + "message": "הזן את הסיסמה של הקובץ", + "description": "Passphrase input placeholder for encrypted import" + }, + "import_pass_hint": { + "message": "זוהי הסיסמה שהגדרת בעת יצירת הגיבוי.", + "description": "Hint under the import passphrase field" + }, + "import_decrypt": { + "message": "פענוח וייבוא", + "description": "Button to decrypt and import an encrypted backup" + }, "import_backup_qr_partly_failed": { - "message": "Import successful, but some QR codes could not be recognized.", + "message": "הייבוא הצליח, אך חלק מקודי ה-QR לא זוהו.", "description": "Import successful, but some QR image cannot be recognized." }, "import_backup_qr_in_batches": { @@ -297,14 +321,106 @@ "message": "השתמש במילוי אוטומטי", "description": "Use Autofill" }, - "use_high_contrast": { - "message": "השתמש בניגודיות גבוהה", - "description": "Use High Contrast" - }, "theme": { "message": "עיצוב", "description": "Theme" }, + "settings_appearance": { + "message": "מראה", + "description": "Settings section: appearance" + }, + "settings_general": { + "message": "כללי", + "description": "Settings section: general" + }, + "edit_accounts": { + "message": "עריכת חשבונות", + "description": "Header title while in edit mode" + }, + "show_qr": { + "message": "הצגת קוד QR", + "description": "Context-menu action to show an account's QR code" + }, + "pin_to_top": { + "message": "הצמד למעלה", + "description": "Context-menu action to pin an account to the top" + }, + "unpin": { + "message": "בטל הצמדה", + "description": "Context-menu action to unpin an account" + }, + "vault_locked": { + "message": "הכספת נעולה", + "description": "Title on the unlock screen" + }, + "unlock": { + "message": "בטל נעילה", + "description": "Unlock button" + }, + "qr_transfer_title": { + "message": "העברת חשבון זה", + "description": "QR sheet caption title" + }, + "qr_transfer_desc": { + "message": "סרוק קוד זה באמצעות OTPilot במכשיר אחר כדי להעביר את החשבון הזה — הוא לעולם לא עוזב את המכשירים שלך.", + "description": "QR sheet caption body" + }, + "backup_on_device": { + "message": "במכשיר זה", + "description": "Backup section: local" + }, + "backup_cloud_sync": { + "message": "סנכרון בענן", + "description": "Backup section: cloud" + }, + "backup_connected": { + "message": "מחובר", + "description": "Cloud provider connected status" + }, + "backup_not_connected": { + "message": "לא מחובר", + "description": "Cloud provider not-connected status" + }, + "showing_codes_for": { + "message": "מציג קודים עבור", + "description": "Smart-filter banner prefix, followed by the site domain" + }, + "other_accounts": { + "message": "חשבונות אחרים", + "description": "Divider above non-matching accounts in the filter view" + }, + "filter_to_site": { + "message": "סנן לפי", + "description": "Banner prefix to re-apply the site filter, followed by the domain" + }, + "onboarding_title": { + "message": "קודים שמלווים אותך לכל מקום", + "description": "Onboarding headline" + }, + "onboarding_subtitle": { + "message": "צור קודי אימות דו-שלביים מאובטחים לכל חשבון — מוצפנים, ללא חיבור לאינטרנט, ותמיד במרחק הקשה אחת מהעתקה.", + "description": "Onboarding subtitle" + }, + "onboarding_feature_offline": { + "message": "פועל לחלוטין ללא חיבור לאינטרנט — שום דבר לא עוזב את המכשיר שלך", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_encrypted": { + "message": "כספת מוצפנת עם נעילת סיסמה אופציונלית", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_scan": { + "message": "סרוק קוד QR או ייבא גיבוי קיים", + "description": "Onboarding feature bullet" + }, + "onboarding_get_started": { + "message": "התחל", + "description": "Onboarding primary button" + }, + "onboarding_import": { + "message": "יש לי גיבוי לייבוא", + "description": "Onboarding secondary button" + }, "theme_light": { "message": "בהיר", "description": "Light theme" @@ -313,26 +429,18 @@ "message": "כהה", "description": "Dark theme" }, - "theme_simple": { - "message": "Simple", - "description": "Simple theme" + "theme_auto": { + "message": "אוטומטי (בהתאם למערכת)", + "description": "Follow the system light/dark setting" }, "theme_compact": { - "message": "Compact", + "message": "קומפקטי", "description": "Compact theme" }, "theme_high_contrast": { "message": "ניגודיות גבוהה", "description": "High Contrast theme" }, - "theme_flat": { - "message": "Flat", - "description": "Flat theme" - }, - "storage_sync_info": { - "message": "גבה אוטומטית את הנתונים שלך לשירותי אחסון של צד ג '.", - "description": "3rd party backup info" - }, "browser_sync": { "message": "סינכרון דפדפן", "description": "Storage location" @@ -342,18 +450,18 @@ "description": "Sign in to 3rd party storage services" }, "sign_in_business": { - "message": "Sign in (Business)", + "message": "התחברות (עסקי)", "description": "Sign in to 3rd party storage services" }, "onedrive_business_perms": { - "message": "Why do business accounts require more permissions?" + "message": "מדוע חשבונות עסקיים דורשים הרשאות נוספות?" }, "log_out": { - "message": "צא מהמערכת", + "message": "התנתקות", "description": "Sign out of 3rd party storage services" }, "token_revoked": { - "message": "הייתה בעיה בהתחברות לחשבון $ SERVICE $ שלך, אנא היכנס שוב.", + "message": "אירעה בעיה בהתחברות לחשבון $SERVICE$ שלך, אנא התחבר שוב.", "description": "Error authenticating to backup service. $SERVICE$ will be replaced with a proper noun (E.g.: 'Google Drive' or 'Dropbox')", "placeholders": { "service": { @@ -362,10 +470,6 @@ } } }, - "otp_unsupported_warn": { - "message": "יש לך חשבון Steam או Blizzard אחד או יותר. גיבויים לא מוצפנים לא ישתמשו בתבנית גיבוי סטנדרטית.", - "description": "Warning if using account that is not supported by standard backup format." - }, "otp_backup_inform": { "message": "ניתן לייבא גיבויים מיישומים אחרים.", "description": "Info text on import page" @@ -407,118 +511,107 @@ "backup": { "message": "גיבוי" }, - "backup_file_info": { - "message": "גיבוי הנתונים שלך לקובץ." - }, "password_policy_default_hint": { - "message": "הסיסמא שסיפקת אינו עומדת בדרישות האבטחה של ארגונך. למידע נוסף, צור קשר עם המנהל." + "message": "הסיסמה שהזנת אינה עומדת בדרישות האבטחה של הארגון שלך. לפרטים נוספים, פנה למנהל המערכת." }, "advisor": { - "message": "Advisor" + "message": "יועץ" }, "advisor_insight_password_not_set": { - "message": "הגדר סיסמא כדי לשמור על המידע שלך." + "message": "הגדר סיסמה כדי להגן על המידע שלך." }, "advisor_insight_auto_lock_not_set": { - "message": "אפשר נעילה אוטמטית כדי לשמור על המידע שלך." + "message": "אפשר נעילה אוטומטית כדי להגן על המידע שלך." }, "advisor_insight_browser_sync_not_enabled": { "message": "סנכרון בין דפדפנים מבוטל. אפשר זאת כדי לאפשר סנכרון חשבונות בין דפדפנים." }, "advisor_insight_auto_fill_not_enabled": { - "message": "Autofill can be enabled to automatically fill codes into websites." + "message": "ניתן להפעיל מילוי אוטומטי כדי למלא קודים באתרים באופן אוטומטי." }, "advisor_insight_smart_filter_not_enabled": { - "message": "Enabling smart filter allows for quick access to accounts." + "message": "הפעלת המסנן החכם מאפשרת גישה מהירה לחשבונות." }, "show_all_insights": { - "message": "Show all insights." + "message": "הצג את כל התובנות." }, "no_insight_available": { - "message": "No insights found, everything looks good!" + "message": "לא נמצאו תובנות, הכול נראה תקין!" }, "danger": { - "message": "Danger" + "message": "סכנה" }, "warning": { - "message": "Warning" + "message": "אזהרה" }, "info": { - "message": "Info" + "message": "מידע" }, "dismiss": { - "message": "Dismiss" + "message": "התעלם" }, "learn_more": { - "message": "Learn more" + "message": "למידע נוסף" }, "enable_context_menu": { - "message": "Add to context menu" + "message": "הוסף לתפריט ההקשר" }, "no_entires": { - "message": "No accounts to display. Add your first account now." + "message": "אין חשבונות להצגה. הוסף את החשבון הראשון שלך עכשיו." }, "permissions": { - "message": "Permissions" + "message": "הרשאות" }, "permission_revoke": { - "message": "Revoke" + "message": "בטל" }, "permission_show_required_permissions": { - "message": "Show non-revocable permissions" + "message": "הצג הרשאות שלא ניתן לבטל" }, "permission_required": { - "message": "This is a required permission and cannot be revoked." + "message": "זוהי הרשאה נדרשת שלא ניתן לבטל." }, "permission_active_tab": { - "message": "Access to the current tab to scan QR codes." + "message": "גישה ללשונית הנוכחית לסריקת קודי QR." }, "permission_storage": { - "message": "Access to browser storage to store account data." + "message": "גישה לאחסון הדפדפן לשמירת נתוני חשבונות." }, "permission_identity": { - "message": "Allows sign in to 3rd party storage services." + "message": "מאפשר התחברות לשירותי אחסון של צד שלישי." }, "permission_alarms": { - "message": "Allows auto-lock to work." + "message": "מאפשר לנעילה האוטומטית לפעול." }, "permission_scripting": { - "message": "Inject scripts into he current tab to scan QR codes and allow auto-fill to work." + "message": "הזרקת סקריפטים ללשונית הנוכחית לסריקת קודי QR ולהפעלת מילוי אוטומטי." }, "permission_clipboard_write": { - "message": "Grants write-only access to the clipboard to copy codes to clipboard when you click on the account." + "message": "מעניק גישת כתיבה בלבד ללוח הגזירה, כדי להעתיק קודים בעת לחיצה על חשבון." }, "permission_context_menus": { - "message": "Adds Authenticator to context menu." + "message": "מוסיף את OTPilot לתפריט ההקשר." }, "permission_sync_clock": { - "message": "Allows clock sync with Google." + "message": "מאפשר סינכרון שעון עם Google." }, "permission_dropbox": { - "message": "Allows backup to Dropbox." + "message": "מאפשר גיבוי ל-Dropbox." }, "permission_dropbox_cannot_revoke": { - "message": "You must disable Dropbox backup first." - }, - "permission_drive": { - "message": "Allows backup to Google Drive." - }, - "permission_drive_cannot_revoke": { - "message": "You must disable Google Drive backup first." - }, - "permission_onedrive": { - "message": "Allows backup to OneDrive." - }, - "permission_onedrive_cannot_revoke": { - "message": "You must disable OneDrive backup first." + "message": "יש לבטל תחילה את גיבוי ה-Dropbox." }, "permission_unknown_permission": { - "message": "Unknown permission. If see this message, please send a bug report." + "message": "הרשאה לא ידועה. אם הודעה זו מוצגת, נא לשלוח דיווח על באג." }, "phrase_wrong": { - "message": "Password incorrect" + "message": "הסיסמה שגויה" }, "activate_auto_filter": { - "message": "Warning: Smart filter loosely matches the domain name to an account. Always verify that you are on the correct website before entering a code!" + "message": "אזהרה: המסנן החכם מבצע התאמה גסה בין שם הדומיין לחשבון. תמיד ודא שאתה נמצא באתר הנכון לפני הזנת קוד!" + }, + "backup_unavailable": { + "message": "עדיין לא זמין", + "description": "Shown on a cloud provider that is not implemented yet." } } diff --git a/_locales/hi/messages.json b/_locales/hi/messages.json index 2f4dc41aa..4879a34d8 100644 --- a/_locales/hi/messages.json +++ b/_locales/hi/messages.json @@ -1,14 +1,22 @@ { "extName": { - "message": "Authentication", + "message": "OTPilot Authenticator", "description": "Extension Name." }, "extShortName": { - "message": "सत्यापन", + "message": "OTPilot", "description": "Extension Short Name." }, + "host": { + "message": "वेबसाइट", + "description": "Bound website host used to restrict autofill to a matching page." + }, + "backup_requires_password": { + "message": "क्लाउड बैकअप का उपयोग करने के लिए पहले एक पासवर्ड सेट करें, ताकि आपके सीक्रेट्स इस डिवाइस से बाहर जाने से पहले ही एन्क्रिप्ट हो जाएं।", + "description": "Shown in the cloud backup section when no master password is set." + }, "extDesc": { - "message": "ऑथेंटिकेटर आपके ब्राउज़र में \"टू-फैक्टर ऑथेंटिकेशन\" कोड जेनरेट करता है।", + "message": "OTPilot Authenticator एन्क्रिप्टेड क्लाउड बैकअप, होस्ट-आधारित स्वतः भरण और सुरक्षा सलाह के साथ टू-फैक्टर (2FA) कोड जेनरेट करता है।", "description": "Extension Description." }, "added": { @@ -16,19 +24,23 @@ "description": "Added Account." }, "errorqr": { - "message": "अपरिचित \"क्यूआर कोड\"।", + "message": "अपरिचित क्यूआर कोड।", "description": "QR Error." }, "errorsecret": { - "message": "अमान्य \"खाता गुप्त\"", + "message": "अमान्य खाता सीक्रेट", "description": "Secret Error." }, + "errorissuer": { + "message": "जारीकर्ता में \"::\" नहीं हो सकता", + "description": "Issuer Error." + }, "add_code": { - "message": "खाता जोड़ो", + "message": "खाता जोड़ें", "description": "Add account." }, "add_qr": { - "message": "स्कैन \"क्यू-आर कोड\"", + "message": "क्यूआर कोड स्कैन करें", "description": "Scan QR Code." }, "add_secret": { @@ -36,17 +48,13 @@ "description": "Manual Entry." }, "migration_fail": { - "message": "आयात विफल. यदि आप Google Authenticator से डेटा माइग्रेट कर रहे हैं, तो कृपया Google प्रमाणक से अपना डेटा पुनः निर्यात करें और पुनः प्रयास करें।", + "message": "आयात विफल. यदि आप Google Authenticator से डेटा माइग्रेट कर रहे हैं, तो कृपया Google Authenticator से अपना डेटा पुनः निर्यात करें और पुनः प्रयास करें।", "description": "Import migration data failed." }, "migration_partly_fail": { "message": "कुछ खाता डेटा सफलतापूर्वक आयात नहीं किया गया था।", "description": "Some migration data is broken." }, - "close": { - "message": "बंद", - "description": "Close." - }, "ok": { "message": "ठीक", "description": "OK." @@ -68,11 +76,11 @@ "description": "Account Name." }, "issuer": { - "message": "ज़ारीकर्ता", + "message": "जारीकर्ता", "description": "Issuer." }, "secret": { - "message": "रहस्य", + "message": "सीक्रेट", "description": "Secret." }, "updateSuccess": { @@ -84,7 +92,7 @@ "description": "Update Failure." }, "about": { - "message": "अबाउट", + "message": "परिचय", "description": "About." }, "settings": { @@ -99,10 +107,6 @@ "message": "वर्तमान पासवर्ड", "description": "Current Passphrase." }, - "new_phrase": { - "message": "नया पासवर्ड", - "description": "New Passphrase." - }, "phrase": { "message": "पासवर्ड", "description": "Passphrase." @@ -120,19 +124,15 @@ "description": "Message that user is required to acknowledge before clearing all data." }, "delete_all": { - "message": "प्रमाणक रीसेट करें" + "message": "OTPilot रीसेट करें" }, "delete_all_warning": { - "message": "यह आपके सभी डेटा को हटा देगा और प्रमाणक को पूरी तरह से रीसेट कर देगा। आप किसी भी हटाए गए डेटा को पुनर्प्राप्त नहीं कर पाएंगे! प्रमाणक को रीसेट करने से पहले आपको एक बैकअप सहेजने पर विचार करना चाहिए।" + "message": "यह आपके सभी डेटा को हटा देगा और OTPilot को पूरी तरह से रीसेट कर देगा। आप किसी भी हटाए गए डेटा को पुनर्प्राप्त नहीं कर पाएंगे! OTPilot को रीसेट करने से पहले आपको एक बैकअप सहेजने पर विचार करना चाहिए।" }, "security_warning": { "message": "इस पासवर्ड का उपयोग आपके खातों को एन्क्रिप्ट करने के लिए किया जाएगा। अगर आप पासवर्ड भूल जाते हैं तो कोई आपकी मदद नहीं कर सकता।", "description": "Passphrase Warning." }, - "update": { - "message": "अपडेट करें", - "description": "Update." - }, "phrase_incorrect": { "message": "जब तक सभी खाते डिक्रिप्ट नहीं हो जाते, तब तक आप एक नया खाता नहीं जोड़ सकते। जारी रखने से पहले कृपया सही पासवर्ड दर्ज करें।", "description": "Passphrase Incorrect." @@ -142,25 +142,21 @@ "description": "Skip Unable-decripted Data." }, "phrase_not_match": { - "message": "गलत पासवर्ड", + "message": "पासवर्ड मेल नहीं खाता।", "description": "Passphrase Not Match." }, "encrypted": { - "message": "गोपित", + "message": "एन्क्रिप्टेड", "description": "Encrypted." }, "copied": { - "message": "कॉपीड", + "message": "कॉपी किया गया", "description": "Copied." }, "feedback": { - "message": "प्रतिपुष्टि", + "message": "प्रतिक्रिया", "description": "Feedback." }, - "translate": { - "message": "अनुवाद करें", - "description": "Translate." - }, "source": { "message": "सोर्स कोड", "description": "Source Code." @@ -173,16 +169,12 @@ "message": "घड़ी को Google के साथ सिंक करें", "description": "Sync Clock" }, - "remember_phrase": { - "message": "पासवर्ड याद रखें", - "description": "Remember Passphrase" - }, "clock_too_far_off": { - "message": "आपकी स्थानीय घड़ी बहुत दूर है, कृपया जारी रखने से पहले इसे ठीक कर लें।", + "message": "सावधान! आपकी स्थानीय घड़ी का समय बहुत गलत है, कृपया आगे बढ़ने से पहले इसे ठीक करें।", "description": "Local Time is Too Far Off" }, "remind_backup": { - "message": "क्या आपके पास अपने खातों का बैकअप है?", + "message": "क्या आपके पास अपने खातों का बैकअप है? देर हो जाने से पहले बैकअप बना लें!", "description": "Remind Backup" }, "capture_failed": { @@ -206,7 +198,7 @@ "description": "Popup Page Settings" }, "scale": { - "message": "परिमाण", + "message": "स्केल", "description": "Scale" }, "export_info": { @@ -230,17 +222,49 @@ "description": "Import qr image backup." }, "import_qr_images": { - "message": "Import QR Images", + "message": "क्यूआर छवियाँ आयात करें", "description": "Import qr images. Shown as add account method." }, "import_backup_code": { - "message": "टेक्स्ट बैकअप आयात करें", + "message": "OTPAuth यूआरआई आयात करें", "description": "Import backup code." }, "import_otp_urls": { - "message": "Import OTP URLs", + "message": "OTPAuth यूआरआई आयात करें", "description": "Import OTP URLs. Shown as add account method." }, + "import_drop_file": { + "message": "बैकअप फ़ाइल यहाँ छोड़ें या ब्राउज़ करने के लिए क्लिक करें", + "description": "File import dropzone title" + }, + "import_file_accepts": { + "message": ".json और .txt एक्सपोर्ट स्वीकार किए जाते हैं", + "description": "File import dropzone hint" + }, + "import_choose_file": { + "message": "फ़ाइल चुनें", + "description": "File import dropzone button" + }, + "import_encrypted_note": { + "message": "यदि फ़ाइल एन्क्रिप्टेड है, तो इसे चुनने के बाद आपसे इसका पासवर्ड पूछा जाएगा।", + "description": "File import note about encrypted backups" + }, + "import_enc_required": { + "message": "एन्क्रिप्टेड — पासवर्ड आवश्यक है", + "description": "Badge on a picked encrypted backup file" + }, + "import_pass_placeholder": { + "message": "फ़ाइल का पासवर्ड दर्ज करें", + "description": "Passphrase input placeholder for encrypted import" + }, + "import_pass_hint": { + "message": "यह वह पासवर्ड है जो आपने बैकअप बनाते समय सेट किया था।", + "description": "Hint under the import passphrase field" + }, + "import_decrypt": { + "message": "डिक्रिप्ट करें और आयात करें", + "description": "Button to decrypt and import an encrypted backup" + }, "import_backup_qr_partly_failed": { "message": "आयात सफल रहा, लेकिन कुछ क्यूआर कोड पहचाने नहीं जा सके।", "description": "Import successful, but some QR image cannot be recognized." @@ -254,7 +278,7 @@ "description": "Show all entries." }, "dropbox_risk": { - "message": "चेतावनी: बैकअप अनएन्क्रिप्टेड हैं। अपने जोखिम पार इस्तेमाल करें।", + "message": "चेतावनी: बैकअप अनएन्क्रिप्टेड हैं। अपने जोखिम पर इस्तेमाल करें।", "description": "Backup risk warning." }, "import_error_password": { @@ -278,7 +302,7 @@ "description": "Search" }, "popout": { - "message": "पॉप अप मोड", + "message": "पॉप-अप मोड", "description": "Make window turn into persistent popup" }, "lock": { @@ -286,36 +310,128 @@ "description": "Lock accounts" }, "edit": { - "message": "संपादित", + "message": "संपादित करें", "description": "Edit" }, "manual_dropbox": { - "message": "मैनुअल बैकअप I", + "message": "मैनुअल बैकअप", "description": "Manual backup" }, "use_autofill": { "message": "स्वतः भरण का उपयोग करें", "description": "Use Autofill" }, - "use_high_contrast": { - "message": "अधिक कंट्रास्ट", - "description": "Use High Contrast" - }, "theme": { "message": "थीम", "description": "Theme" }, + "settings_appearance": { + "message": "रूप-रंग", + "description": "Settings section: appearance" + }, + "settings_general": { + "message": "सामान्य", + "description": "Settings section: general" + }, + "edit_accounts": { + "message": "खाते संपादित करें", + "description": "Header title while in edit mode" + }, + "show_qr": { + "message": "क्यूआर कोड दिखाएं", + "description": "Context-menu action to show an account's QR code" + }, + "pin_to_top": { + "message": "शीर्ष पर पिन करें", + "description": "Context-menu action to pin an account to the top" + }, + "unpin": { + "message": "पिन हटाएं", + "description": "Context-menu action to unpin an account" + }, + "vault_locked": { + "message": "वॉल्ट लॉक है", + "description": "Title on the unlock screen" + }, + "unlock": { + "message": "अनलॉक करें", + "description": "Unlock button" + }, + "qr_transfer_title": { + "message": "इस खाते को स्थानांतरित करें", + "description": "QR sheet caption title" + }, + "qr_transfer_desc": { + "message": "इस खाते को दूसरे डिवाइस पर ले जाने के लिए वहां OTPilot से इस कोड को स्कैन करें — यह आपके डिवाइस से कभी बाहर नहीं जाता।", + "description": "QR sheet caption body" + }, + "backup_on_device": { + "message": "इस डिवाइस पर", + "description": "Backup section: local" + }, + "backup_cloud_sync": { + "message": "क्लाउड सिंक", + "description": "Backup section: cloud" + }, + "backup_connected": { + "message": "कनेक्टेड", + "description": "Cloud provider connected status" + }, + "backup_not_connected": { + "message": "कनेक्टेड नहीं है", + "description": "Cloud provider not-connected status" + }, + "showing_codes_for": { + "message": "इसके लिए कोड दिखाए जा रहे हैं", + "description": "Smart-filter banner prefix, followed by the site domain" + }, + "other_accounts": { + "message": "अन्य खाते", + "description": "Divider above non-matching accounts in the filter view" + }, + "filter_to_site": { + "message": "इस पर फ़िल्टर करें", + "description": "Banner prefix to re-apply the site filter, followed by the domain" + }, + "onboarding_title": { + "message": "आपके साथ चलने वाले कोड", + "description": "Onboarding headline" + }, + "onboarding_subtitle": { + "message": "हर खाते के लिए सुरक्षित टू-फैक्टर कोड जेनरेट करें — एन्क्रिप्टेड, ऑफ़लाइन, और हमेशा एक टैप में कॉपी करने के लिए तैयार।", + "description": "Onboarding subtitle" + }, + "onboarding_feature_offline": { + "message": "पूरी तरह ऑफ़लाइन काम करता है — कुछ भी आपके डिवाइस से बाहर नहीं जाता", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_encrypted": { + "message": "वैकल्पिक पासवर्ड लॉक के साथ एन्क्रिप्टेड वॉल्ट", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_scan": { + "message": "क्यूआर कोड स्कैन करें या मौजूदा बैकअप आयात करें", + "description": "Onboarding feature bullet" + }, + "onboarding_get_started": { + "message": "शुरू करें", + "description": "Onboarding primary button" + }, + "onboarding_import": { + "message": "मेरे पास आयात करने के लिए एक बैकअप है", + "description": "Onboarding secondary button" + }, "theme_light": { - "message": "हल्की", + "message": "लाइट", "description": "Light theme" }, "theme_dark": { "message": "डार्क", "description": "Dark theme" }, - "theme_simple": { - "message": "सरल", - "description": "Simple theme" + "theme_auto": { + "message": "ऑटो", + "description": "Follow the system light/dark setting" }, "theme_compact": { "message": "सघन", @@ -325,20 +441,12 @@ "message": "अधिक कंट्रास्ट", "description": "High Contrast theme" }, - "theme_flat": { - "message": "सपाट", - "description": "Flat theme" - }, - "storage_sync_info": { - "message": "अपने डेटा को तृतीय पक्ष संग्रहण सेवाओं में स्वचालित रूप से बैकअप लें।", - "description": "3rd party backup info" - }, "browser_sync": { "message": "ब्राउज़र सिंक", "description": "Storage location" }, "sign_in": { - "message": "अंदर आएं", + "message": "साइन इन करें", "description": "Sign in to 3rd party storage services" }, "sign_in_business": { @@ -362,10 +470,6 @@ } } }, - "otp_unsupported_warn": { - "message": "आपके पास एक या अधिक स्टीम या बर्फ़ीला तूफ़ान खाते हैं। अनएन्क्रिप्टेड बैकअप मानकीकृत बैकअप प्रारूप का उपयोग नहीं करेंगे।", - "description": "Warning if using account that is not supported by standard backup format." - }, "otp_backup_inform": { "message": "आप कुछ अन्य एप्लिकेशन से बैकअप आयात कर सकते हैं।", "description": "Info text on import page" @@ -378,7 +482,7 @@ "message": "लोड हो रहा है..." }, "autolock": { - "message": "के बाद लॉक ऐप" + "message": "इतने समय बाद लॉक करें" }, "minutes": { "message": "मिनट" @@ -407,9 +511,6 @@ "backup": { "message": "बैकअप" }, - "backup_file_info": { - "message": "एक फ़ाइल में अपने डेटा का बैकअप लें।" - }, "password_policy_default_hint": { "message": "आपका पासवर्ड आपके संगठन की सुरक्षा आवश्यकताओं को पूरा नहीं करता है। अधिक जानकारी के लिए अपने व्यवस्थापक से संपर्क करें।" }, @@ -447,13 +548,13 @@ "message": "जानकारी" }, "dismiss": { - "message": "खारिज" + "message": "खारिज करें" }, "learn_more": { "message": "और अधिक जानें" }, "enable_context_menu": { - "message": "प्रसंग मेनू में जोड़ें" + "message": "संदर्भ मेनू में जोड़ें" }, "no_entires": { "message": "प्रदर्शित करने के लिए कोई खाता नहीं है। अपना पहला खाता अभी जोड़ें।" @@ -462,13 +563,13 @@ "message": "अनुमतियां" }, "permission_revoke": { - "message": "रद्द करना" + "message": "रद्द करें" }, "permission_show_required_permissions": { - "message": "गैर-प्रतिसंहरणीय अनुमतियां दिखाएं" + "message": "गैर-रद्द करने योग्य अनुमतियां दिखाएं" }, "permission_required": { - "message": "यह एक आवश्यक अनुमति है और इसे निरस्त नहीं किया जा सकता है।" + "message": "यह एक आवश्यक अनुमति है और इसे रद्द नहीं किया जा सकता है।" }, "permission_active_tab": { "message": "क्यूआर कोड स्कैन करने के लिए वर्तमान टैब तक पहुंचें।" @@ -480,45 +581,37 @@ "message": "तृतीय पक्ष संग्रहण सेवाओं में साइन इन करने की अनुमति देता है।" }, "permission_alarms": { - "message": "Allows auto-lock to work." + "message": "ऑटो-लॉक को काम करने की अनुमति देता है।" }, "permission_scripting": { - "message": "Inject scripts into he current tab to scan QR codes and allow auto-fill to work." + "message": "क्यूआर कोड स्कैन करने और स्वतः भरण को सक्षम करने के लिए वर्तमान टैब में स्क्रिप्ट इंजेक्ट करता है।" }, "permission_clipboard_write": { "message": "जब आप खाते पर क्लिक करते हैं तो कोड को क्लिपबोर्ड पर कॉपी करने के लिए क्लिपबोर्ड पर केवल-लिखने की पहुंच प्रदान करता है।" }, "permission_context_menus": { - "message": "संदर्भ मेनू में प्रमाणक जोड़ता है।" + "message": "संदर्भ मेनू में OTPilot जोड़ता है।" }, "permission_sync_clock": { "message": "घड़ी को Google के साथ सिंक करने की अनुमति देता है।" }, "permission_dropbox": { - "message": "Dropbox बैकअप की अनुमति देता है" + "message": "Dropbox बैकअप की अनुमति देता है।" }, "permission_dropbox_cannot_revoke": { "message": "आपको पहले Dropbox बैकअप को अक्षम करना होगा।" }, - "permission_drive": { - "message": "Google Drive पर बैकअप की अनुमति देता है।" - }, - "permission_drive_cannot_revoke": { - "message": "आपको पहले Google Drive बैकअप को अक्षम करना होगा।" - }, - "permission_onedrive": { - "message": "OneDrive पर बैकअप की अनुमति देता है।" - }, - "permission_onedrive_cannot_revoke": { - "message": "आपको पहले OneDrive बैकअप को अक्षम करना होगा।" - }, "permission_unknown_permission": { "message": "अज्ञात अनुमति। यदि आप यह संदेश देखते हैं, तो कृपया एक बग रिपोर्ट भेजें।" }, "phrase_wrong": { - "message": "Password incorrect" + "message": "पासवर्ड गलत है।" }, "activate_auto_filter": { - "message": "Warning: Smart filter loosely matches the domain name to an account. Always verify that you are on the correct website before entering a code!" + "message": "चेतावनी: स्मार्ट फ़िल्टर डोमेन नाम को किसी खाते से मोटे तौर पर मिलाता है। कोड दर्ज करने से पहले हमेशा सुनिश्चित करें कि आप सही वेबसाइट पर हैं!" + }, + "backup_unavailable": { + "message": "अभी उपलब्ध नहीं है", + "description": "Shown on a cloud provider that is not implemented yet." } } diff --git a/_locales/hr/messages.json b/_locales/hr/messages.json index fd27ec502..62fdfd527 100644 --- a/_locales/hr/messages.json +++ b/_locales/hr/messages.json @@ -1,14 +1,22 @@ { "extName": { - "message": "Autentifikator", + "message": "OTPilot Authenticator", "description": "Extension Name." }, "extShortName": { - "message": "Autentifikator", + "message": "OTPilot", "description": "Extension Short Name." }, + "host": { + "message": "Web-mjesto", + "description": "Bound website host used to restrict autofill to a matching page." + }, + "backup_requires_password": { + "message": "Najprije postavi lozinku za korištenje sigurnosne kopije u oblaku, tako da se tvoje tajne šifriraju prije nego što napuste ovaj uređaj.", + "description": "Shown in the cloud backup section when no master password is set." + }, "extDesc": { - "message": "Autentifikator generira dvofaktorske autentifikacijske kodove u tvom pregledniku.", + "message": "OTPilot Authenticator generira dvofaktorske (2FA) kodove, sa šifriranom sigurnosnom kopijom u oblaku, automatskim ispunjavanjem povezanim s web-mjestom i sigurnosnim savjetima.", "description": "Extension Description." }, "added": { @@ -23,12 +31,16 @@ "message": "Neispravna tajna računa", "description": "Secret Error." }, + "errorissuer": { + "message": "Izdavatelj ne smije sadržavati „::”.", + "description": "Issuer Error." + }, "add_code": { "message": "Dodaj račun", "description": "Add account." }, "add_qr": { - "message": "Snimi QR kod", + "message": "Skeniraj QR kod", "description": "Scan QR Code." }, "add_secret": { @@ -36,17 +48,13 @@ "description": "Manual Entry." }, "migration_fail": { - "message": "Uvoz nije uspio. Ako seliš podatke iz Google Autentifikatora, ponovo izvezi svoje podatke iz Google Autentifikatora i pokušaj ponovo.", + "message": "Uvoz nije uspio. Ako seliš podatke iz aplikacije Google Authenticator, ponovo izvezi svoje podatke iz Google Authenticator i pokušaj ponovo.", "description": "Import migration data failed." }, "migration_partly_fail": { "message": "Neki podaci računa nisu uspješno uvezeni.", "description": "Some migration data is broken." }, - "close": { - "message": "Zatvori", - "description": "Close." - }, "ok": { "message": "U redu", "description": "OK." @@ -99,10 +107,6 @@ "message": "Trenutačna lozinka", "description": "Current Passphrase." }, - "new_phrase": { - "message": "Nova lozinka", - "description": "New Passphrase." - }, "phrase": { "message": "Lozinka", "description": "Passphrase." @@ -120,25 +124,21 @@ "description": "Message that user is required to acknowledge before clearing all data." }, "delete_all": { - "message": "Resetiraj Autentifikator" + "message": "Resetiraj OTPilot" }, "delete_all_warning": { - "message": "Ovo će izbrisati sve tvoje podatke i potpuno resetirati Autentifikator. Nećeš moći vratiti izbrisane podatke! Spremi sigurnosnu kopiju prije resetiranja Autentifikatora." + "message": "Ovo će izbrisati sve tvoje podatke i potpuno resetirati OTPilot. Nećeš moći vratiti izbrisane podatke! Razmisli o spremanju sigurnosne kopije prije resetiranja OTPilota." }, "security_warning": { "message": "Ova će se lozinka koristiti za šifriranje tvog računa. Nitko ti neće moći pomoći ako ju zaboraviš.", "description": "Passphrase Warning." }, - "update": { - "message": "Aktualiziranje", - "description": "Update." - }, "phrase_incorrect": { "message": "Ne možeš dodati novi račun, sve dok svi računi nisu dešifrirani. Za nastavljanje upiši ispravnu lozinku.", "description": "Passphrase Incorrect." }, "phrase_incorrect_export": { - "message": "Računi koji se nisu mogli dešifrirati neće biti uključeni u ovoj sigurnosnoj kopiji.", + "message": "Računi koji se nisu mogli dešifrirati neće biti uključeni u ovu sigurnosnu kopiju.", "description": "Skip Unable-decripted Data." }, "phrase_not_match": { @@ -157,10 +157,6 @@ "message": "Povratne informacije", "description": "Feedback." }, - "translate": { - "message": "Prevedi", - "description": "Translate." - }, "source": { "message": "Izvorni kod", "description": "Source Code." @@ -173,16 +169,12 @@ "message": "Sinkroniziraj sat s Google-om", "description": "Sync Clock" }, - "remember_phrase": { - "message": "Zapamti lozinku", - "description": "Remember Passphrase" - }, "clock_too_far_off": { - "message": "Pažnja! Tvoj lokalni sat nije usklađen. Najmjesti ga prije nastavljanja.", + "message": "Pažnja! Tvoj lokalni sat previše odstupa. Uskladi ga prije nastavljanja.", "description": "Local Time is Too Far Off" }, "remind_backup": { - "message": "Imaš sigurnosne kopije za tvoje račune? Spremi ih dok nije prekasno!", + "message": "Imaš li sigurnosnu kopiju svojih računa? Ne čekaj dok ne bude prekasno!", "description": "Remind Backup" }, "capture_failed": { @@ -190,7 +182,7 @@ "description": "Capture Failed" }, "capture_local_file_failed": { - "message": "Pokušavaš snimiti QR kod iz lokalne datoteke? Umjeto toga koristi „Uvoz sigurnosne kopije slike QR koda”.", + "message": "Pokušavaš skenirati QR kod iz lokalne datoteke? Umjesto toga koristi „Uvoz sigurnosne kopije slike QR koda”.", "description": "Import QR image backup instead of scan local image" }, "based_on_time": { @@ -210,7 +202,7 @@ "description": "Scale" }, "export_info": { - "message": "Upozorenje: sve sigurnosne kopije su nešifrirane. Želiš li dodati račun u jednu drugu aplikaciju? Prijeđi mišem preko gornjeg desnog dijela svakog računa i pritisni gumb tipku za skrivanje.", + "message": "Upozorenje: sve sigurnosne kopije su nešifrirane. Želiš li dodati račun u drugu aplikaciju? Prijeđi mišem preko gornjeg desnog dijela bilo kojeg računa i pritisni skriveni gumb.", "description": "Export menu info text" }, "download_backup": { @@ -234,13 +226,45 @@ "description": "Import qr images. Shown as add account method." }, "import_backup_code": { - "message": "Uvoz sigurnosne kopije teksta", + "message": "Uvoz OTPAuth URI-ja", "description": "Import backup code." }, "import_otp_urls": { - "message": "Uvoz OTP URL-ova", + "message": "Uvoz OTPAuth URI-ja", "description": "Import OTP URLs. Shown as add account method." }, + "import_drop_file": { + "message": "Ispusti datoteku sigurnosne kopije ili klikni za pregled", + "description": "File import dropzone title" + }, + "import_file_accepts": { + "message": "Prihvaća izvoze u .json i .txt formatu", + "description": "File import dropzone hint" + }, + "import_choose_file": { + "message": "Odaberi datoteku", + "description": "File import dropzone button" + }, + "import_encrypted_note": { + "message": "Ako je datoteka šifrirana, nakon odabira ćeš morati unijeti njezinu lozinku.", + "description": "File import note about encrypted backups" + }, + "import_enc_required": { + "message": "Šifrirano — potrebna lozinka", + "description": "Badge on a picked encrypted backup file" + }, + "import_pass_placeholder": { + "message": "Upiši lozinku datoteke", + "description": "Passphrase input placeholder for encrypted import" + }, + "import_pass_hint": { + "message": "Ovo je lozinka postavljena prilikom izrade sigurnosne kopije.", + "description": "Hint under the import passphrase field" + }, + "import_decrypt": { + "message": "Dešifriraj i uvezi", + "description": "Button to decrypt and import an encrypted backup" + }, "import_backup_qr_partly_failed": { "message": "Uvoz je uspio, ali neki QR kodovi nisu prepoznati.", "description": "Import successful, but some QR image cannot be recognized." @@ -254,7 +278,7 @@ "description": "Show all entries." }, "dropbox_risk": { - "message": "Upozorenje: sigurnose kopije nisu šifrirane. Koristi ih na vlastitu odgovornost.", + "message": "Upozorenje: sigurnosne kopije nisu šifrirane. Koristi ih na vlastitu odgovornost.", "description": "Backup risk warning." }, "import_error_password": { @@ -270,7 +294,7 @@ "description": "Remove password." }, "download_enc_backup": { - "message": "Prezimanje sigurnosne kopije zaštićene lozinkom", + "message": "Preuzmi sigurnosnu kopiju zaštićenu lozinkom", "description": "Download Encrypted Backup" }, "search": { @@ -290,21 +314,113 @@ "description": "Edit" }, "manual_dropbox": { - "message": "Ručno spremljena sigurnosna kopija", + "message": "Ručna sigurnosna kopija", "description": "Manual backup" }, "use_autofill": { "message": "Koristi automatsko ispunjavanje", "description": "Use Autofill" }, - "use_high_contrast": { - "message": "Koristi visoki kontrast", - "description": "Use High Contrast" - }, "theme": { "message": "Tema", "description": "Theme" }, + "settings_appearance": { + "message": "Izgled", + "description": "Settings section: appearance" + }, + "settings_general": { + "message": "Općenito", + "description": "Settings section: general" + }, + "edit_accounts": { + "message": "Uredi račune", + "description": "Header title while in edit mode" + }, + "show_qr": { + "message": "Prikaži QR kod", + "description": "Context-menu action to show an account's QR code" + }, + "pin_to_top": { + "message": "Prikvači na vrh", + "description": "Context-menu action to pin an account to the top" + }, + "unpin": { + "message": "Otkvači", + "description": "Context-menu action to unpin an account" + }, + "vault_locked": { + "message": "Trezor zaključan", + "description": "Title on the unlock screen" + }, + "unlock": { + "message": "Otključaj", + "description": "Unlock button" + }, + "qr_transfer_title": { + "message": "Prenesi ovaj račun", + "description": "QR sheet caption title" + }, + "qr_transfer_desc": { + "message": "Skeniraj ovaj kod OTPilotom na drugom uređaju da preneseš ovaj račun — nikada ne napušta tvoje uređaje.", + "description": "QR sheet caption body" + }, + "backup_on_device": { + "message": "Na ovom uređaju", + "description": "Backup section: local" + }, + "backup_cloud_sync": { + "message": "Sinkronizacija u oblaku", + "description": "Backup section: cloud" + }, + "backup_connected": { + "message": "Povezano", + "description": "Cloud provider connected status" + }, + "backup_not_connected": { + "message": "Nije povezano", + "description": "Cloud provider not-connected status" + }, + "showing_codes_for": { + "message": "Prikaz kodova za", + "description": "Smart-filter banner prefix, followed by the site domain" + }, + "other_accounts": { + "message": "Ostali računi", + "description": "Divider above non-matching accounts in the filter view" + }, + "filter_to_site": { + "message": "Filtriraj na", + "description": "Banner prefix to re-apply the site filter, followed by the domain" + }, + "onboarding_title": { + "message": "Kodovi koji putuju s tobom", + "description": "Onboarding headline" + }, + "onboarding_subtitle": { + "message": "Generiraj sigurne dvofaktorske kodove za svaki račun — šifrirano, offline i uvijek spremno za kopiranje jednim dodirom.", + "description": "Onboarding subtitle" + }, + "onboarding_feature_offline": { + "message": "Radi potpuno offline — ništa ne napušta tvoj uređaj", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_encrypted": { + "message": "Šifrirani trezor s opcionalnim zaključavanjem lozinkom", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_scan": { + "message": "Skeniraj QR kod ili uvezi postojeću sigurnosnu kopiju", + "description": "Onboarding feature bullet" + }, + "onboarding_get_started": { + "message": "Započni", + "description": "Onboarding primary button" + }, + "onboarding_import": { + "message": "Imam sigurnosnu kopiju za uvoz", + "description": "Onboarding secondary button" + }, "theme_light": { "message": "Svijetla", "description": "Light theme" @@ -313,9 +429,9 @@ "message": "Tamna", "description": "Dark theme" }, - "theme_simple": { - "message": "Jednostavna", - "description": "Simple theme" + "theme_auto": { + "message": "Automatski", + "description": "Follow the system light/dark setting" }, "theme_compact": { "message": "Kompaktna", @@ -325,14 +441,6 @@ "message": "Visoki kontrast", "description": "High Contrast theme" }, - "theme_flat": { - "message": "Plošna", - "description": "Flat theme" - }, - "storage_sync_info": { - "message": "Automatski spremi sigurnosnu kopiju tvojih podataka u stranoj usluzi za spremanje podataka.", - "description": "3rd party backup info" - }, "browser_sync": { "message": "Sinkronizacija preglednika", "description": "Storage location" @@ -362,10 +470,6 @@ } } }, - "otp_unsupported_warn": { - "message": "Imaš jedan ili više Steam ili Blizzard računa. Nešifrirane sigurnosne kopije neće koristiti standardni format sigurnosne kopije.", - "description": "Warning if using account that is not supported by standard backup format." - }, "otp_backup_inform": { "message": "Možeš uvesti sigurnosne kopije iz drugih aplikacija.", "description": "Info text on import page" @@ -375,7 +479,7 @@ "description": "learn more link on import page. Placed after otp_backup_inform" }, "loading": { - "message": "Učitavanje …" + "message": "Učitavanje…" }, "autolock": { "message": "Zaključaj nakon" @@ -407,9 +511,6 @@ "backup": { "message": "Sigurnosna kopija" }, - "backup_file_info": { - "message": "Spremi sigurnosnu kopiju tvojih podataka u datoteku." - }, "password_policy_default_hint": { "message": "Tvoja lozinka ne zadovoljava sigurnosne zahtjeve tvoje organizacije. Kontaktiraj administratora za daljnje informacije." }, @@ -426,7 +527,7 @@ "message": "Sinkronizacija preglednika je deaktivirana. Aktiviranje omogućuje sinkronizaciju računa u svim preglednicima." }, "advisor_insight_auto_fill_not_enabled": { - "message": "Automatsko ispunjavanje se može aktivirati za automatsko ispunjavanje kodova na web stranicama." + "message": "Automatsko ispunjavanje možeš uključiti kako bi se kodovi automatski unosili na web stranice." }, "advisor_insight_smart_filter_not_enabled": { "message": "Aktiviranje pametnog filtra omogućuje brzi pristup računima." @@ -471,10 +572,10 @@ "message": "Ovo je obavezna dozvola i ne može se opozvati." }, "permission_active_tab": { - "message": "Pristupi trenutačnoj kartici za snimanje QR kodova." + "message": "Pristup trenutačnoj kartici za skeniranje QR kodova." }, "permission_storage": { - "message": "Pristupi memoriji preglednika za spremanje podataka računa." + "message": "Pristup memoriji preglednika za spremanje podataka računa." }, "permission_identity": { "message": "Dozvoljava prijavu na strane usluge spremanja podataka." @@ -483,13 +584,13 @@ "message": "Omogućuje automatsko zaključavanje." }, "permission_scripting": { - "message": "Umetni skripta u trenutačnu karticu za skeniranje QR kodova i omogući automatsko ispunjavanje." + "message": "Umeće skripte u trenutačnu karticu za skeniranje QR kodova i omogućuje rad automatskog ispunjavanja." }, "permission_clipboard_write": { "message": "Odobrava pristup samo-za-pisanje međuspremniku za kopiranje kodova u međuspremnik kada pritisneš račun." }, "permission_context_menus": { - "message": "Dodaje Autentifikator u kontekstni izbornik." + "message": "Dodaje OTPilot u kontekstni izbornik." }, "permission_sync_clock": { "message": "Dozvoljava sinkroniziranje sata s Googleom." @@ -500,25 +601,17 @@ "permission_dropbox_cannot_revoke": { "message": "Najprije moraš deaktivirati Dropbox sigurnosne kopije." }, - "permission_drive": { - "message": "Dozvoljava spremanje sigurnosne kopije na Google Driveu." - }, - "permission_drive_cannot_revoke": { - "message": "Najprije moraš deaktivirati Google Drive sigurnosne kopije." - }, - "permission_onedrive": { - "message": "Dozvoljava spremanje sigurnosne kopije na OneDriveu." - }, - "permission_onedrive_cannot_revoke": { - "message": "Najprije moraš deaktivirati OneDrive sigurnosne kopije." - }, "permission_unknown_permission": { - "message": "Nepoznata dozvola. Ako vidiš ovu poruku pošalji izvještaj o grešci." + "message": "Nepoznata dozvola. Ako vidiš ovu poruku, pošalji izvještaj o grešci." }, "phrase_wrong": { "message": "Neispravna lozinka" }, "activate_auto_filter": { - "message": "Upozorenje: Pametni filtar slabo povezuje naziv domene s računom. Prije unosa koda uvijek provjerite jeste li na ispravnoj web stranici!" + "message": "Upozorenje: Pametni filtar slabo povezuje naziv domene s računom. Prije unosa koda uvijek provjeri jesi li na ispravnoj web stranici!" + }, + "backup_unavailable": { + "message": "Još nije dostupno", + "description": "Shown on a cloud provider that is not implemented yet." } } diff --git a/_locales/hu/messages.json b/_locales/hu/messages.json index 98b01572a..7fafd99cf 100644 --- a/_locales/hu/messages.json +++ b/_locales/hu/messages.json @@ -1,14 +1,22 @@ { "extName": { - "message": "Hitelesítő", + "message": "OTPilot Authenticator", "description": "Extension Name." }, "extShortName": { - "message": "Hitelesítő", + "message": "OTPilot", "description": "Extension Short Name." }, + "host": { + "message": "Webhely", + "description": "Bound website host used to restrict autofill to a matching page." + }, + "backup_requires_password": { + "message": "Először állítson be jelszót a felhőalapú biztonsági mentés használatához – a titkos kulcsai már titkosítva hagyják el ezt az eszközt.", + "description": "Shown in the cloud backup section when no master password is set." + }, "extDesc": { - "message": "A Hitelesítő kétlépcsős ellenőrző kódokat generál a böngészőben.", + "message": "Az OTPilot Authenticator kétlépcsős (2FA) kódokat generál, titkosított felhőalapú biztonsági mentéssel, webhelyhez igazított automatikus kitöltéssel és biztonsági tanácsokkal.", "description": "Extension Description." }, "added": { @@ -20,9 +28,13 @@ "description": "QR Error." }, "errorsecret": { - "message": "Invalid account secret", + "message": "A fiók titkos kulcsa érvénytelen", "description": "Secret Error." }, + "errorissuer": { + "message": "A kibocsátó nem tartalmazhat „::” karaktersorozatot", + "description": "Issuer Error." + }, "add_code": { "message": "Fiók hozzáadása", "description": "Add account." @@ -36,17 +48,13 @@ "description": "Manual Entry." }, "migration_fail": { - "message": "Az importálás sikertelen. Ha a Google Hitelesítőből történt az adatok mozgatása, akkor próbálkozzon a Google Hitelesítőből az adatok újra exportálásával!", + "message": "Az importálás sikertelen volt. Ha a Google Authenticatorból migrál adatokat, exportálja újra az adatait a Google Authenticatorból, és próbálja meg újra.", "description": "Import migration data failed." }, "migration_partly_fail": { - "message": "A fiók adatok importálása csak részben volt sikeres.", + "message": "A fiókadatok importálása csak részben volt sikeres.", "description": "Some migration data is broken." }, - "close": { - "message": "Bezár", - "description": "Close." - }, "ok": { "message": "Rendben", "description": "OK." @@ -84,7 +92,7 @@ "description": "Update Failure." }, "about": { - "message": "Rólunk", + "message": "Névjegy", "description": "About." }, "settings": { @@ -99,10 +107,6 @@ "message": "Jelenlegi jelszó", "description": "Current Passphrase." }, - "new_phrase": { - "message": "Új jelszó", - "description": "New Passphrase." - }, "phrase": { "message": "Jelszó", "description": "Passphrase." @@ -120,21 +124,17 @@ "description": "Message that user is required to acknowledge before clearing all data." }, "delete_all": { - "message": "Hitelesítő visszaállítása" + "message": "OTPilot visszaállítása" }, "delete_all_warning": { - "message": "\nEzzel törli az összes adatot, és teljesen visszaállítja a Hitelesítőt. A törölt adatokat nem fogja tudni visszaállítani! Az Authenticator alaphelyzetbe állítása előtt fontolóra kell vennie egy biztonsági másolat készítését." + "message": "Ez törli az összes adatát, és teljesen visszaállítja az OTPilotot. A törölt adatokat nem fogja tudni visszaállítani! Érdemes biztonsági mentést készítenie, mielőtt visszaállítja az OTPilotot." }, "security_warning": { "message": "Ez a jelszó lesz használva a fiókjai titkosításához. Senki sem tud segíteni, ha elfelejti a jelszavát.", "description": "Passphrase Warning." }, - "update": { - "message": "Frissítés", - "description": "Update." - }, "phrase_incorrect": { - "message": "Nem adhat hozzá új fiókot vagy exportálhat adatot míg az összes fiók titkosítva van. Kérjük, írja be a helyes jelszót a folytatáshoz.", + "message": "Új fiókot csak akkor adhat hozzá, ha az összes fiók vissza van fejtve. Kérjük, írja be a helyes jelszót a folytatáshoz.", "description": "Passphrase Incorrect." }, "phrase_incorrect_export": { @@ -142,7 +142,7 @@ "description": "Skip Unable-decripted Data." }, "phrase_not_match": { - "message": "A jelszók nem egyeznek.", + "message": "A jelszavak nem egyeznek.", "description": "Passphrase Not Match." }, "encrypted": { @@ -157,10 +157,6 @@ "message": "Visszajelzés", "description": "Feedback." }, - "translate": { - "message": "Fordítás", - "description": "Translate." - }, "source": { "message": "Forráskód", "description": "Source Code." @@ -170,19 +166,15 @@ "description": "Passphrase Info" }, "sync_clock": { - "message": "Előzmények szinkronizálása Google-val", + "message": "Óra szinkronizálása Google-lel", "description": "Sync Clock" }, - "remember_phrase": { - "message": "Jelszó megjegyzése", - "description": "Remember Passphrase" - }, "clock_too_far_off": { "message": "Vigyázat! A helyi óra túl pontatlan, javítsa a folytatás előtt.", "description": "Local Time is Too Far Off" }, "remind_backup": { - "message": "Készített már biztonsági mentést a fiókjaihoz? Ne várjon még túl késő lesz!", + "message": "Készített már biztonsági mentést a fiókjairól? Ne várjon, amíg túl késő lesz!", "description": "Remind Backup" }, "capture_failed": { @@ -190,7 +182,7 @@ "description": "Capture Failed" }, "capture_local_file_failed": { - "message": "Helyben tárolt fájlból próbálja beolvasni a QR-kódot? Inkább importálja a mentett QR képet!", + "message": "Helyben tárolt fájlból próbálja beolvasni a QR-kódot? Inkább használja a „QR kód biztonsági mentés importálása” funkciót!", "description": "Import QR image backup instead of scan local image" }, "based_on_time": { @@ -202,7 +194,7 @@ "description": "Counter Based" }, "resize_popup_page": { - "message": "Tulajdonságok", + "message": "Beállítások", "description": "Popup Page Settings" }, "scale": { @@ -210,7 +202,7 @@ "description": "Scale" }, "export_info": { - "message": "Figyelem: a mentések nem titkosítottak. Hozzá szeretne adni egy fiókot egy másik apphoz? Tartsa az egeret bármely fiók jobb felső sarkánál, majd nyomja meg a titkos gombot.", + "message": "Figyelem: a mentések nem titkosítottak. Hozzá szeretne adni egy fiókot egy másik alkalmazáshoz? Tartsa az egeret bármely fiók jobb felső sarkánál, majd nyomja meg a rejtett gombot.", "description": "Export menu info text" }, "download_backup": { @@ -230,23 +222,55 @@ "description": "Import qr image backup." }, "import_qr_images": { - "message": "Import QR Images", + "message": "QR-képek importálása", "description": "Import qr images. Shown as add account method." }, "import_backup_code": { - "message": "Szöveges biztonsági mentés importálása", + "message": "OTPAuth URI importálása", "description": "Import backup code." }, "import_otp_urls": { - "message": "Import OTP URLs", + "message": "OTPAuth URI importálása", "description": "Import OTP URLs. Shown as add account method." }, + "import_drop_file": { + "message": "Húzza ide a biztonsági mentés fájlját, vagy kattintson a tallózáshoz", + "description": "File import dropzone title" + }, + "import_file_accepts": { + "message": "Elfogadott formátumok: .json és .txt exportfájlok", + "description": "File import dropzone hint" + }, + "import_choose_file": { + "message": "Fájl kiválasztása", + "description": "File import dropzone button" + }, + "import_encrypted_note": { + "message": "Ha a fájl titkosított, a kiválasztása után a rendszer bekéri a jelszavát.", + "description": "File import note about encrypted backups" + }, + "import_enc_required": { + "message": "Titkosítva — jelszó szükséges", + "description": "Badge on a picked encrypted backup file" + }, + "import_pass_placeholder": { + "message": "Adja meg a fájl jelszavát", + "description": "Passphrase input placeholder for encrypted import" + }, + "import_pass_hint": { + "message": "Ez az a jelszó, amelyet a biztonsági mentés készítésekor adott meg.", + "description": "Hint under the import passphrase field" + }, + "import_decrypt": { + "message": "Visszafejtés és importálás", + "description": "Button to decrypt and import an encrypted backup" + }, "import_backup_qr_partly_failed": { "message": "Sikeresen importálva, de néhány QR-kód nem ismerhető fel.", "description": "Import successful, but some QR image cannot be recognized." }, "import_backup_qr_in_batches": { - "message": "Kijelölhet több képfájlt is a biztonsági mentés folyamatos importálásához.", + "message": "Egyszerre több fájlt is kiválaszthat, hogy kötegelve importálja a biztonsági mentést.", "description": "You can select multiple image files to import backup in batches." }, "show_all_entries": { @@ -262,7 +286,7 @@ "description": "Error password warning when import backups." }, "local_passphrase_warning": { - "message": "Az ön jelszava helyben van tárolva, kérem módosítsa a biztonsági menüben sürgősen.", + "message": "Az Ön jelszava helyben van tárolva, kérjük, módosítsa haladéktalanul a biztonsági menüben.", "description": "localStorage password warning." }, "remove": { @@ -297,14 +321,106 @@ "message": "Automatikus kitöltés használata", "description": "Use Autofill" }, - "use_high_contrast": { - "message": "Nagy kontraszt használata", - "description": "Use High Contrast" - }, "theme": { "message": "Téma", "description": "Theme" }, + "settings_appearance": { + "message": "Megjelenés", + "description": "Settings section: appearance" + }, + "settings_general": { + "message": "Általános", + "description": "Settings section: general" + }, + "edit_accounts": { + "message": "Fiókok szerkesztése", + "description": "Header title while in edit mode" + }, + "show_qr": { + "message": "QR-kód megjelenítése", + "description": "Context-menu action to show an account's QR code" + }, + "pin_to_top": { + "message": "Kitűzés felülre", + "description": "Context-menu action to pin an account to the top" + }, + "unpin": { + "message": "Kitűzés visszavonása", + "description": "Context-menu action to unpin an account" + }, + "vault_locked": { + "message": "A tároló zárolva", + "description": "Title on the unlock screen" + }, + "unlock": { + "message": "Feloldás", + "description": "Unlock button" + }, + "qr_transfer_title": { + "message": "Fiók átvitele", + "description": "QR sheet caption title" + }, + "qr_transfer_desc": { + "message": "Olvassa be ezt a kódot az OTPilot alkalmazással egy másik eszközön a fiók átviteléhez — az adat soha nem hagyja el az eszközeit.", + "description": "QR sheet caption body" + }, + "backup_on_device": { + "message": "Ezen az eszközön", + "description": "Backup section: local" + }, + "backup_cloud_sync": { + "message": "Felhőszinkronizálás", + "description": "Backup section: cloud" + }, + "backup_connected": { + "message": "Csatlakozva", + "description": "Cloud provider connected status" + }, + "backup_not_connected": { + "message": "Nincs csatlakoztatva", + "description": "Cloud provider not-connected status" + }, + "showing_codes_for": { + "message": "Kódok megjelenítve ehhez:", + "description": "Smart-filter banner prefix, followed by the site domain" + }, + "other_accounts": { + "message": "Egyéb fiókok", + "description": "Divider above non-matching accounts in the filter view" + }, + "filter_to_site": { + "message": "Szűrés erre:", + "description": "Banner prefix to re-apply the site filter, followed by the domain" + }, + "onboarding_title": { + "message": "Kódok, amelyek önnel tartanak", + "description": "Onboarding headline" + }, + "onboarding_subtitle": { + "message": "Biztonságos kétlépcsős kódokat generál minden fiókhoz — titkosítva, offline, és mindig egy érintésnyire a másolástól.", + "description": "Onboarding subtitle" + }, + "onboarding_feature_offline": { + "message": "Teljesen offline működik — semmi sem hagyja el az eszközét", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_encrypted": { + "message": "Titkosított tároló, választható jelszavas zárral", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_scan": { + "message": "QR-kód beolvasása vagy meglévő biztonsági mentés importálása", + "description": "Onboarding feature bullet" + }, + "onboarding_get_started": { + "message": "Kezdés", + "description": "Onboarding primary button" + }, + "onboarding_import": { + "message": "Van egy biztonsági mentésem, amit importálni szeretnék", + "description": "Onboarding secondary button" + }, "theme_light": { "message": "Világos", "description": "Light theme" @@ -313,28 +429,20 @@ "message": "Sötét", "description": "Dark theme" }, - "theme_simple": { - "message": "Egyszerű téma", - "description": "Simple theme" + "theme_auto": { + "message": "Automatikus", + "description": "Follow the system light/dark setting" }, "theme_compact": { - "message": "Kompakt téma", + "message": "Kompakt", "description": "Compact theme" }, "theme_high_contrast": { "message": "Nagy kontrasztú", "description": "High Contrast theme" }, - "theme_flat": { - "message": "Flat", - "description": "Flat theme" - }, - "storage_sync_info": { - "message": "Automatikusan menti az ön adatait a harmadik fél által üzemeltetett tároló szolgáltatásnál.", - "description": "3rd party backup info" - }, "browser_sync": { - "message": "Böngésző szinronizálása", + "message": "Böngésző szinkronizálása", "description": "Storage location" }, "sign_in": { @@ -362,12 +470,8 @@ } } }, - "otp_unsupported_warn": { - "message": "Egy vagy több Steam vagy Blizzard fiókokkal rendelkezik. A titkosítatlan biztonsági mentések nem fogják használni a szabványos biztonsági mentési formátumot.", - "description": "Warning if using account that is not supported by standard backup format." - }, "otp_backup_inform": { - "message": "Imprtálhat biztonsági mentéseket pár másik alkalmazásból.", + "message": "Importálhat biztonsági mentéseket pár másik alkalmazásból.", "description": "Info text on import page" }, "otp_backup_learn": { @@ -381,7 +485,7 @@ "message": "Lezárás ennyi idő után" }, "minutes": { - "message": "percek" + "message": "perc" }, "advanced": { "message": "Haladó" @@ -396,7 +500,7 @@ "message": "Érvénytelen" }, "digits": { - "message": "Számok" + "message": "Számjegyek" }, "algorithm": { "message": "Algoritmus" @@ -407,11 +511,8 @@ "backup": { "message": "Biztonsági mentés" }, - "backup_file_info": { - "message": "Mentse el adatait egy fájlba!" - }, "password_policy_default_hint": { - "message": "A jelszava nem fele meg az előírt biztonsági követelményeknek. Vegye fel a kapcsolatot a rendszergazdával további információk érdekében!" + "message": "A jelszava nem felel meg a szervezete biztonsági követelményeinek. További információért forduljon a rendszergazdához." }, "advisor": { "message": "Tanácsadó" @@ -420,13 +521,13 @@ "message": "Állítson be jelszót az adatai biztonsága érdekében!" }, "advisor_insight_auto_lock_not_set": { - "message": "Automatikus lezárás engedélyezése adatai védelmének érdekében." + "message": "Engedélyezze az automatikus lezárást az adatai védelme érdekében." }, "advisor_insight_browser_sync_not_enabled": { "message": "A böngésző szinkronizálás le van tiltva. Az engedélyezés a fiókok böngészők közötti szinkronizálást teszi lehetővé." }, "advisor_insight_auto_fill_not_enabled": { - "message": "Az automatikus kitöltés engedélyezésével a kódok az internet oldalak mezőiben automatikusan kitültésre kerülnek." + "message": "Az automatikus kitöltés engedélyezésével a kódok automatikusan kitöltésre kerülnek a weboldalak mezőiben." }, "advisor_insight_smart_filter_not_enabled": { "message": "Az okos szűrő engedélyezésével a fiókok gyors elérése válik lehetővé." @@ -435,7 +536,7 @@ "message": "Összes tipp mutatása." }, "no_insight_available": { - "message": "Nem található tipp, úgy tűnik minden rendben!" + "message": "Nem található tipp, úgy tűnik, minden rendben!" }, "danger": { "message": "Veszély" @@ -450,13 +551,13 @@ "message": "Elvet" }, "learn_more": { - "message": "Tudj meg többet" + "message": "További információ" }, "enable_context_menu": { - "message": "Kontextus menühöz adás" + "message": "Hozzáadás a helyi menühöz" }, "no_entires": { - "message": "Nincs megjeleníthető fiók. Add hozzá első fiókodat most." + "message": "Nincs megjeleníthető fiók. Adja hozzá az első fiókját most." }, "permissions": { "message": "Engedélyek" @@ -480,45 +581,37 @@ "message": "Bejelentkezés engedélyezése harmadik fél által biztosított tárhely szolgáltatáshoz." }, "permission_alarms": { - "message": "Allows auto-lock to work." + "message": "Lehetővé teszi az automatikus zárolás működését." }, "permission_scripting": { - "message": "Inject scripts into he current tab to scan QR codes and allow auto-fill to work." + "message": "Szkriptek befecskendezése az aktuális fülbe a QR-kódok beolvasásához és az automatikus kitöltés működéséhez." }, "permission_clipboard_write": { "message": "A vágólapon csak írásra jogosító engedélyt biztosít, hogy a kódok a vágólapra kerüljenek a fiókra való kattintáskor." }, "permission_context_menus": { - "message": "Hitelesítő hozzáadása tartalomfüggő menühöz." + "message": "OTPilot hozzáadása a helyi menühöz." }, "permission_sync_clock": { "message": "Óra szinkronizálás engedélyezése Google által." }, "permission_dropbox": { - "message": "Biztonsági mentés engedélyezése DropBox-ra." + "message": "Biztonsági mentés engedélyezése a Dropboxra." }, "permission_dropbox_cannot_revoke": { - "message": "Először tiltsa le a DropBox-ra történő biztonsági adatmentést!" - }, - "permission_drive": { - "message": "Biztonsági adatmentés Google Drive-ra." - }, - "permission_drive_cannot_revoke": { - "message": "Először tiltsa le a Google Drive-ra történő biztonsági adatmentést!" - }, - "permission_onedrive": { - "message": "Biztonsági adatmentés engedélyezése OneDrive-ra." - }, - "permission_onedrive_cannot_revoke": { - "message": "Először tiltsa le a OneDrive-ra történő biztonsági adatmentést!" + "message": "Először tiltsa le a Dropboxra történő biztonsági mentést!" }, "permission_unknown_permission": { "message": "Ismeretlen engedély. Ha ez az üzenet jelenik meg, kérem küldjön hibajelentést!" }, "phrase_wrong": { - "message": "Password incorrect" + "message": "Hibás jelszó" }, "activate_auto_filter": { - "message": "Warning: Smart filter loosely matches the domain name to an account. Always verify that you are on the correct website before entering a code!" + "message": "Figyelem: az okos szűrő csak hozzávetőlegesen illeszti a domainnevet egy fiókhoz. Mindig ellenőrizze, hogy a megfelelő weboldalon van-e, mielőtt megadna egy kódot!" + }, + "backup_unavailable": { + "message": "Még nem elérhető", + "description": "Shown on a cloud provider that is not implemented yet." } } diff --git a/_locales/hy/messages.json b/_locales/hy/messages.json index c7c4ccf13..7ccacc0e2 100644 --- a/_locales/hy/messages.json +++ b/_locales/hy/messages.json @@ -1,14 +1,22 @@ { "extName": { - "message": "Authenticator", + "message": "OTPilot Authenticator", "description": "Extension Name." }, "extShortName": { - "message": "Authenticator", + "message": "OTPilot", "description": "Extension Short Name." }, + "host": { + "message": "Կայք", + "description": "Bound website host used to restrict autofill to a matching page." + }, + "backup_requires_password": { + "message": "Նախ սահմանեք գաղտնաբառ՝ ամպային կրկնօրինակումն օգտագործելու համար, որպեսզի Ձեր գաղտնիքները գաղտնագրվեն նախքան այս սարքից դուրս գալը։", + "description": "Shown in the cloud backup section when no master password is set." + }, "extDesc": { - "message": "Authenticator generates two-factor authentication codes in your browser.", + "message": "OTPilot Authenticator-ը ստեղծում է երկգործոն նույնականացման (2FA) կոդեր՝ գաղտնագրված ամպային կրկնօրինակմամբ, կայքին համապատասխանեցված ավտոմուտքագրմամբ և անվտանգության խորհուրդներով։", "description": "Extension Description." }, "added": { @@ -23,6 +31,10 @@ "message": "«Գաղտնիք» դաշտը անվավեր է։", "description": "Secret Error." }, + "errorissuer": { + "message": "Թողարկիչը չի կարող պարունակել «::»", + "description": "Issuer Error." + }, "add_code": { "message": "Ավելացնել հաշիվ", "description": "Add account." @@ -43,12 +55,8 @@ "message": "Հաշվի տվյալների ներմուծումը մասամբ ձախողվեց։", "description": "Some migration data is broken." }, - "close": { - "message": "Փակել", - "description": "Close." - }, "ok": { - "message": "Պարզ է", + "message": "Լավ", "description": "OK." }, "yes": { @@ -92,23 +100,19 @@ "description": "Settings." }, "security": { - "message": "Անվտանգության", + "message": "Անվտանգություն", "description": "Security." }, "current_phrase": { - "message": "Այժմյա Գաղտնաբառ", + "message": "Ընթացիկ Գաղտնաբառ", "description": "Current Passphrase." }, - "new_phrase": { - "message": "Նոր Գաղտնաբառ", - "description": "New Passphrase." - }, "phrase": { "message": "Գաղտնաբառ", "description": "Passphrase." }, "confirm_phrase": { - "message": "Նոր Գաղտնաբառ (Կրկին)", + "message": "Հաստատեք Գաղտնաբառը", "description": "Confirm Passphrase." }, "confirm_delete": { @@ -120,19 +124,15 @@ "description": "Message that user is required to acknowledge before clearing all data." }, "delete_all": { - "message": "Զրոյացնել «Authenticator»-ը" + "message": "Զրոյացնել «OTPilot»-ը" }, "delete_all_warning": { - "message": "Այս գործողության արդյունքում վերջնականապես կջնջվեն Ձեր բոլոր տվյալները և «Authenticator»-ի կարգավորումները։ Զրոյացում կատարելուց առաջ խորհուրդ է տրվում կատարել կրկնօրինակում։" + "message": "Այս գործողության արդյունքում վերջնականապես կջնջվեն Ձեր բոլոր տվյալները և «OTPilot»-ի կարգավորումները։ Զրոյացում կատարելուց առաջ խորհուրդ է տրվում կատարել կրկնօրինակում։" }, "security_warning": { "message": "Այս գաղտնաբառը կօգտագործվի Ձեր բոլոր հաշիվները կոդավորելու համար։ Ոչ ոք Ձեզ չի կարող օգնել, եթե հանկարծ մոռանաք այն։", "description": "Passphrase Warning." }, - "update": { - "message": "Թարմացնել", - "description": "Update." - }, "phrase_incorrect": { "message": "Բոլոր հաշիվները պետք է ապակոդավորվեն՝ նախքան նոր հաշիվ ավելացնելը: Շարունակելուց առաջ՝ նախ մուտքագրեք ճիշտ գաղտնաբառը:", "description": "Passphrase Incorrect." @@ -157,10 +157,6 @@ "message": "Հետադարձ Կապ", "description": "Feedback." }, - "translate": { - "message": "Թարգմանել", - "description": "Translate." - }, "source": { "message": "Ելակետային Կոդ", "description": "Source Code." @@ -173,10 +169,6 @@ "message": "Համաժամեցնել Google-ից", "description": "Sync Clock" }, - "remember_phrase": { - "message": "Հիշել գաղտնաբառերը", - "description": "Remember Passphrase" - }, "clock_too_far_off": { "message": "Զգուշացում՝ Ձեր լոկալ ժամացույցը չափազանց սխալ է։ Խնդրում ենք ուղղել այն՝ շարունակելուց առաջ։", "description": "Local Time is Too Far Off" @@ -230,7 +222,7 @@ "description": "Import qr image backup." }, "import_qr_images": { - "message": "Import QR Images", + "message": "Ներմուծել QR Պատկերներ", "description": "Import qr images. Shown as add account method." }, "import_backup_code": { @@ -238,9 +230,41 @@ "description": "Import backup code." }, "import_otp_urls": { - "message": "Import OTP URLs", + "message": "Ներմուծել OTPAuth URI", "description": "Import OTP URLs. Shown as add account method." }, + "import_drop_file": { + "message": "Քաշեք կրկնօրինակման ֆայլը այստեղ կամ սեղմեք՝ ընտրելու համար", + "description": "File import dropzone title" + }, + "import_file_accepts": { + "message": "Ընդունում է .json և .txt ձևաչափի արտահանված ֆայլեր", + "description": "File import dropzone hint" + }, + "import_choose_file": { + "message": "Ընտրել ֆայլ", + "description": "File import dropzone button" + }, + "import_encrypted_note": { + "message": "Եթե ֆայլը գաղտնագրված է, ընտրելուց հետո Ձեզանից կպահանջվի մուտքագրել դրա գաղտնաբառը։", + "description": "File import note about encrypted backups" + }, + "import_enc_required": { + "message": "Գաղտնագրված — պահանջվում է գաղտնաբառ", + "description": "Badge on a picked encrypted backup file" + }, + "import_pass_placeholder": { + "message": "Մուտքագրեք ֆայլի գաղտնաբառը", + "description": "Passphrase input placeholder for encrypted import" + }, + "import_pass_hint": { + "message": "Սա այն գաղտնաբառն է, որը սահմանել եք կրկնօրինակումը ստեղծելիս։", + "description": "Hint under the import passphrase field" + }, + "import_decrypt": { + "message": "Ապակոդավորել և ներմուծել", + "description": "Button to decrypt and import an encrypted backup" + }, "import_backup_qr_partly_failed": { "message": "Ներմուծումը հաջողությամբ կատարվեց։ Որոշ QR-կոդեր չեն ճանաչվել։", "description": "Import successful, but some QR image cannot be recognized." @@ -254,7 +278,7 @@ "description": "Show all entries." }, "dropbox_risk": { - "message": "Զգուշացում՝ Կրկնօրինակումները չեն գաղտնագրվում։", + "message": "Զգուշացում՝ կրկնօրինակումները չեն գաղտնագրվում։ Օգտագործեք ի սեփական պատասխանատվություն։", "description": "Backup risk warning." }, "import_error_password": { @@ -270,7 +294,7 @@ "description": "Remove password." }, "download_enc_backup": { - "message": "Ներբեռնել Կոդավորված Կրկնօրինակում", + "message": "Ներբեռնել Գաղտնագրված Կրկնօրինակում", "description": "Download Encrypted Backup" }, "search": { @@ -297,25 +321,117 @@ "message": "Ավտոմուտքագրում", "description": "Use Autofill" }, - "use_high_contrast": { - "message": "Բարձր Կոնտրաստ", - "description": "Use High Contrast" - }, "theme": { "message": "Գեղատեսք", "description": "Theme" }, + "settings_appearance": { + "message": "Տեսք", + "description": "Settings section: appearance" + }, + "settings_general": { + "message": "Ընդհանուր", + "description": "Settings section: general" + }, + "edit_accounts": { + "message": "Խմբագրել հաշիվները", + "description": "Header title while in edit mode" + }, + "show_qr": { + "message": "Ցույց տալ QR կոդը", + "description": "Context-menu action to show an account's QR code" + }, + "pin_to_top": { + "message": "Ամրակցել վերևում", + "description": "Context-menu action to pin an account to the top" + }, + "unpin": { + "message": "Ապակցել", + "description": "Context-menu action to unpin an account" + }, + "vault_locked": { + "message": "Պահոցը կողպված է", + "description": "Title on the unlock screen" + }, + "unlock": { + "message": "Ապակողպել", + "description": "Unlock button" + }, + "qr_transfer_title": { + "message": "Փոխանցել այս հաշիվը", + "description": "QR sheet caption title" + }, + "qr_transfer_desc": { + "message": "Սկանավորեք այս կոդը մեկ այլ սարքի «OTPilot»-ով՝ այս հաշիվը փոխանցելու համար — այն երբեք դուրս չի գալիս Ձեր սարքերից։", + "description": "QR sheet caption body" + }, + "backup_on_device": { + "message": "Այս սարքում", + "description": "Backup section: local" + }, + "backup_cloud_sync": { + "message": "Ամպային համաժամեցում", + "description": "Backup section: cloud" + }, + "backup_connected": { + "message": "Կապակցված է", + "description": "Cloud provider connected status" + }, + "backup_not_connected": { + "message": "Կապակցված չէ", + "description": "Cloud provider not-connected status" + }, + "showing_codes_for": { + "message": "Ցուցադրվում են կոդերը՝", + "description": "Smart-filter banner prefix, followed by the site domain" + }, + "other_accounts": { + "message": "Այլ հաշիվներ", + "description": "Divider above non-matching accounts in the filter view" + }, + "filter_to_site": { + "message": "Զտել ըստ՝", + "description": "Banner prefix to re-apply the site filter, followed by the domain" + }, + "onboarding_title": { + "message": "Կոդեր, որոնք միշտ Ձեզ հետ են", + "description": "Onboarding headline" + }, + "onboarding_subtitle": { + "message": "Ստեղծեք անվտանգ երկգործոն կոդեր յուրաքանչյուր հաշվի համար՝ գաղտնագրված, անցանց և միշտ մեկ հպումով պատճենվող։", + "description": "Onboarding subtitle" + }, + "onboarding_feature_offline": { + "message": "Աշխատում է լիովին անցանց — ոչինչ դուրս չի գալիս Ձեր սարքից", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_encrypted": { + "message": "Գաղտնագրված պահոց՝ ընտրովի գաղտնաբառային կողպումով", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_scan": { + "message": "Սկանավորեք QR կոդ կամ ներմուծեք առկա կրկնօրինակում", + "description": "Onboarding feature bullet" + }, + "onboarding_get_started": { + "message": "Սկսել", + "description": "Onboarding primary button" + }, + "onboarding_import": { + "message": "Ես ունեմ ներմուծելու կրկնօրինակում", + "description": "Onboarding secondary button" + }, "theme_light": { - "message": "Առավոտյան", + "message": "Բաց", "description": "Light theme" }, "theme_dark": { - "message": "Գիշերային", + "message": "Մուգ", "description": "Dark theme" }, - "theme_simple": { - "message": "Հասարակ", - "description": "Simple theme" + "theme_auto": { + "message": "Ինքնաշխատ", + "description": "Follow the system light/dark setting" }, "theme_compact": { "message": "Կոմպակտ", @@ -325,14 +441,6 @@ "message": "Բարձր Կոնտրաստ", "description": "High Contrast theme" }, - "theme_flat": { - "message": "Հարթ", - "description": "Flat theme" - }, - "storage_sync_info": { - "message": "Ավտոկրկնօրինակեք Ձեր տվյալները՝ հիշողության ծառայություններում։", - "description": "3rd party backup info" - }, "browser_sync": { "message": "Կայքզննարկիչի Համաժամեցում", "description": "Storage location" @@ -362,10 +470,6 @@ } } }, - "otp_unsupported_warn": { - "message": "Ձեր հաշիվների ցանկի մեջ մտնում է «Steam» կամ «Blizzard» ծառայության հաշիվ։ Ոչ-կոդավորված կրկնօրինակումները չեն օգտագործի ստանդարտ ձևաչափ։", - "description": "Warning if using account that is not supported by standard backup format." - }, "otp_backup_inform": { "message": "Կարող եք ներմուծել կրկնօրինակումներ այլ հավելվածներից։", "description": "Info text on import page" @@ -384,7 +488,7 @@ "message": "րոպեից" }, "advanced": { - "message": "Կոմպլեքս" + "message": "Ընդլայնված" }, "period": { "message": "Ժամանակաշրջան" @@ -407,9 +511,6 @@ "backup": { "message": "Կրկնօրինակում" }, - "backup_file_info": { - "message": "Արխիվացրեք Ձեր տվյալները՝ ֆայլի մեջ։" - }, "password_policy_default_hint": { "message": "Ձեր գաղտնաբառը չի հետևում կազմակերպության կողմից սահմանված կանոններին։ Հարցերի դեպքում՝ կապնվեք Ձեր ադմինիստրատորի հետ։" }, @@ -444,10 +545,10 @@ "message": "Զգուշացում" }, "info": { - "message": "Ծանուցում" + "message": "Տեղեկություն" }, "dismiss": { - "message": "Ջնջել" + "message": "Անտեսել" }, "learn_more": { "message": "Իմանալ ավելին" @@ -462,7 +563,7 @@ "message": "Թույլտվություններ" }, "permission_revoke": { - "message": "Զրոյացնել" + "message": "Հետկանչել" }, "permission_show_required_permissions": { "message": "Ցույց տալ անչեղարկելի թույլտվությունները" @@ -477,19 +578,19 @@ "message": "Կայքզննարկիչի հիշողության օգտագործում՝ որպես հաշիվների տվյալների պահեստ" }, "permission_identity": { - "message": "Թույլատրում է մուտք գործել հիշողության ծառայություններ" + "message": "Թույլատրում է մուտք գործել երրորդ կողմի պահեստավորման ծառայություններ" }, "permission_alarms": { - "message": "Allows auto-lock to work." + "message": "Թույլատրում է ավտոկողպումի աշխատանքը" }, "permission_scripting": { - "message": "Inject scripts into he current tab to scan QR codes and allow auto-fill to work." + "message": "Ներարկում է սկրիպտներ ընթացիկ ներդիրում՝ QR կոդերը սկանավորելու և ավտոմուտքագրումը հնարավոր դարձնելու համար" }, "permission_clipboard_write": { - "message": "Թույլատրում է փոփոխություններ կատարել կցատարում՝ հաշիվների կոդերը պատճենելու համար" + "message": "Տրամադրում է միայն գրառման հասանելիություն փոխանակման բուֆերին՝ հաշվի վրա սեղմելիս կոդը այնտեղ պատճենելու համար" }, "permission_context_menus": { - "message": "Ավելացնում է «Authenticator»-ը՝ կոնտեքստ ընտրացանկում" + "message": "Ավելացնում է «OTPilot»-ը՝ կոնտեքստ ընտրացանկում" }, "permission_sync_clock": { "message": "Թույլատրում է համաժամեցում՝ «Google»-ից" @@ -500,25 +601,17 @@ "permission_dropbox_cannot_revoke": { "message": "Նախ՝ անջատեք «Dropbox»-ում կրկնօրինակումը։" }, - "permission_drive": { - "message": "Թույլատրում է կրկնօրինակել «Google Drive»-ում" - }, - "permission_drive_cannot_revoke": { - "message": "Նախ՝ անջատեք «Google Drive»-ում կրկնօրինակումը։" - }, - "permission_onedrive": { - "message": "Թույլատրում է կրկնօրինակել «OneDrive»-ում" - }, - "permission_onedrive_cannot_revoke": { - "message": "Նախ՝ անջատեք «OneDrive»-ում կրկնօրինակումը։" - }, "permission_unknown_permission": { "message": "Անհայտ թույլտվություն։ Խնդրում ենք կապնվել ծրագրի մշակողների հետ։" }, "phrase_wrong": { - "message": "Password incorrect" + "message": "Գաղտնաբառը սխալ է" }, "activate_auto_filter": { - "message": "Warning: Smart filter loosely matches the domain name to an account. Always verify that you are on the correct website before entering a code!" + "message": "Զգուշացում՝ Խելացի ֆիլտրը մոտավորապես համապատասխանեցնում է տիրույթի անվանումը հաշվի հետ։ Միշտ ստուգեք, որ գտնվում եք ճիշտ կայքում՝ նախքան կոդը մուտքագրելը։" + }, + "backup_unavailable": { + "message": "Դեռ հասանելի չէ", + "description": "Shown on a cloud provider that is not implemented yet." } } diff --git a/_locales/id/messages.json b/_locales/id/messages.json index 4172e26c9..a2a12cd86 100644 --- a/_locales/id/messages.json +++ b/_locales/id/messages.json @@ -1,14 +1,22 @@ { "extName": { - "message": "Autentikator", + "message": "OTPilot Authenticator", "description": "Extension Name." }, "extShortName": { - "message": "Authenticator", + "message": "OTPilot", "description": "Extension Short Name." }, + "host": { + "message": "Situs Web", + "description": "Bound website host used to restrict autofill to a matching page." + }, + "backup_requires_password": { + "message": "Atur kata sandi terlebih dahulu untuk menggunakan cadangan cloud, sehingga data rahasia Anda dienkripsi sebelum meninggalkan perangkat ini.", + "description": "Shown in the cloud backup section when no master password is set." + }, "extDesc": { - "message": "Authenticator menghasilkan kode verifikasi 2 langkah di browser anda.", + "message": "OTPilot Authenticator menghasilkan kode verifikasi dua faktor (2FA), dilengkapi cadangan cloud terenkripsi, isi otomatis sesuai situs, dan saran keamanan.", "description": "Extension Description." }, "added": { @@ -20,9 +28,13 @@ "description": "QR Error." }, "errorsecret": { - "message": "Nama account tidak valid", + "message": "Rahasia akun tidak valid", "description": "Secret Error." }, + "errorissuer": { + "message": "Penerbit tidak boleh mengandung \"::\"", + "description": "Issuer Error." + }, "add_code": { "message": "Tambah Akun", "description": "Add account." @@ -43,10 +55,6 @@ "message": "Beberapa data akun tidak berhasil diimpor.", "description": "Some migration data is broken." }, - "close": { - "message": "Tutup", - "description": "Close." - }, "ok": { "message": "Ok", "description": "OK." @@ -99,10 +107,6 @@ "message": "Kata sandi saat ini", "description": "Current Passphrase." }, - "new_phrase": { - "message": "Kata sandi baru", - "description": "New Passphrase." - }, "phrase": { "message": "Kata sandi", "description": "Passphrase." @@ -112,7 +116,7 @@ "description": "Confirm Passphrase." }, "confirm_delete": { - "message": "Apakah anda yakin ingin menghapusnya? Tindakan ini tidak dapat dibatalkan.", + "message": "Apakah Anda yakin ingin menghapus akun ini? Tindakan ini tidak dapat dibatalkan.", "description": "Remove entry confirmation" }, "confirm_delete_all": { @@ -120,29 +124,25 @@ "description": "Message that user is required to acknowledge before clearing all data." }, "delete_all": { - "message": "Reset Authenticator" + "message": "Reset OTPilot" }, "delete_all_warning": { - "message": "Tindakan ini akan menghapus semua data dan mereset ulang Autentikator. Anda tidak dapat mengembalikan data yang sudah terhapus! Pastikan anda membuat Backup terlebih dahulu sebelum mereset ulang Authentikator." + "message": "Tindakan ini akan menghapus semua data Anda dan mereset OTPilot sepenuhnya. Anda tidak akan dapat memulihkan data yang sudah dihapus! Sebaiknya buat cadangan terlebih dahulu sebelum mereset OTPilot." }, "security_warning": { - "message": "Kata sandi ini akan digunakan untuk mengenkripsi kode rahasia anda. Tidak ada yang dapat membantu anda jikalau anda lupa kata sandi anda.", + "message": "Kata sandi ini akan digunakan untuk mengenkripsi akun Anda. Tidak ada yang dapat membantu Anda jika Anda lupa kata sandi ini.", "description": "Passphrase Warning." }, - "update": { - "message": "Perbaharui", - "description": "Update." - }, "phrase_incorrect": { - "message": "Anda tidak dapat menambahkan akun baru atau mengekspor data hingga semua akun dienkripsi. Tolong masukkan kata sandi yang tepat sebelum melanjutkan.", + "message": "Anda tidak dapat menambahkan akun baru hingga semua akun didekripsi. Silakan masukkan kata sandi yang benar sebelum melanjutkan.", "description": "Passphrase Incorrect." }, "phrase_incorrect_export": { - "message": "Akun yang tidak dapat di dekripsi tidak akan disertakan dalam cadangan ini.", + "message": "Akun yang tidak dapat didekripsi tidak akan disertakan dalam cadangan ini.", "description": "Skip Unable-decripted Data." }, "phrase_not_match": { - "message": "Kata sandi salah.", + "message": "Kata sandi tidak cocok.", "description": "Passphrase Not Match." }, "encrypted": { @@ -157,10 +157,6 @@ "message": "Umpan Balik", "description": "Feedback." }, - "translate": { - "message": "Terjemahkan", - "description": "Translate." - }, "source": { "message": "Kode sumber", "description": "Source Code." @@ -170,27 +166,23 @@ "description": "Passphrase Info" }, "sync_clock": { - "message": "Sinkronisasi riwayat dengan Google", + "message": "Sinkronisasi jam dengan Google", "description": "Sync Clock" }, - "remember_phrase": { - "message": "Ingat kata sandi", - "description": "Remember Passphrase" - }, "clock_too_far_off": { - "message": "Hati hati! Jam lokal anda terlalu jauh, tolong perbarui sebelum melanjutkan.", + "message": "Hati-hati! Jam lokal Anda meleset terlalu jauh, segera perbarui sebelum melanjutkan.", "description": "Local Time is Too Far Off" }, "remind_backup": { - "message": "Apakah anda ingin mencadangkan data anda? Jangan menunggu hingga terlambat!", + "message": "Apakah Anda ingin mencadangkan data Anda? Jangan menunggu hingga terlambat!", "description": "Remind Backup" }, "capture_failed": { - "message": "Gagal ditangkap, silakan muat ulang halaman dan coba lagi.", + "message": "Pengambilan gagal, silakan muat ulang halaman dan coba lagi.", "description": "Capture Failed" }, "capture_local_file_failed": { - "message": "Apakah Anda mencoba memindai kode QR dari file lokal? Gunakan cadangan Impor QR Image.", + "message": "Apakah Anda mencoba memindai kode QR dari berkas lokal? Gunakan Impor Cadangan Gambar QR.", "description": "Import QR image backup instead of scan local image" }, "based_on_time": { @@ -234,19 +226,51 @@ "description": "Import qr images. Shown as add account method." }, "import_backup_code": { - "message": "Impor Text Cadangan", + "message": "Impor URI OTPAuth", "description": "Import backup code." }, "import_otp_urls": { "message": "Impor URL OTP", "description": "Import OTP URLs. Shown as add account method." }, + "import_drop_file": { + "message": "Seret berkas cadangan ke sini atau klik untuk menelusuri", + "description": "File import dropzone title" + }, + "import_file_accepts": { + "message": "Menerima ekspor .json dan .txt", + "description": "File import dropzone hint" + }, + "import_choose_file": { + "message": "Pilih berkas", + "description": "File import dropzone button" + }, + "import_encrypted_note": { + "message": "Jika berkas terenkripsi, Anda akan diminta memasukkan kata sandinya setelah memilihnya.", + "description": "File import note about encrypted backups" + }, + "import_enc_required": { + "message": "Terenkripsi — kata sandi diperlukan", + "description": "Badge on a picked encrypted backup file" + }, + "import_pass_placeholder": { + "message": "Masukkan kata sandi berkas ini", + "description": "Passphrase input placeholder for encrypted import" + }, + "import_pass_hint": { + "message": "Ini adalah kata sandi yang Anda atur saat membuat cadangan.", + "description": "Hint under the import passphrase field" + }, + "import_decrypt": { + "message": "Dekripsi & impor", + "description": "Button to decrypt and import an encrypted backup" + }, "import_backup_qr_partly_failed": { "message": "Impor berhasil, tapi beberapa gambar QR tidak dapat dikenali.", "description": "Import successful, but some QR image cannot be recognized." }, "import_backup_qr_in_batches": { - "message": "Anda bisa memilih beberapa berkas untuk cadangan impor di batch.", + "message": "Anda dapat memilih beberapa berkas untuk mengimpor cadangan sekaligus.", "description": "You can select multiple image files to import backup in batches." }, "show_all_entries": { @@ -254,7 +278,7 @@ "description": "Show all entries." }, "dropbox_risk": { - "message": "Peringatan: Cadangan tidak terenkripsi. Perhatikan resikonya sebelum digunakan.", + "message": "Peringatan: Cadangan tidak terenkripsi. Perhatikan risikonya sebelum digunakan.", "description": "Backup risk warning." }, "import_error_password": { @@ -262,7 +286,7 @@ "description": "Error password warning when import backups." }, "local_passphrase_warning": { - "message": "Kata sandi anda disimpan secara lokal, tolong ubah kedalam menu keamanan segera.", + "message": "Kata sandi Anda disimpan secara lokal, segera ubah di menu keamanan.", "description": "localStorage password warning." }, "remove": { @@ -270,7 +294,7 @@ "description": "Remove password." }, "download_enc_backup": { - "message": "Download cadangan terenkripsi", + "message": "Unduh Cadangan Terenkripsi", "description": "Download Encrypted Backup" }, "search": { @@ -290,21 +314,113 @@ "description": "Edit" }, "manual_dropbox": { - "message": "Backup manual", + "message": "Cadangan manual", "description": "Manual backup" }, "use_autofill": { - "message": "IsiOtomatis", + "message": "Gunakan Isi Otomatis", "description": "Use Autofill" }, - "use_high_contrast": { - "message": "Gunakan Kontras yang Tinggi", - "description": "Use High Contrast" - }, "theme": { "message": "Tema", "description": "Theme" }, + "settings_appearance": { + "message": "Tampilan", + "description": "Settings section: appearance" + }, + "settings_general": { + "message": "Umum", + "description": "Settings section: general" + }, + "edit_accounts": { + "message": "Ubah akun", + "description": "Header title while in edit mode" + }, + "show_qr": { + "message": "Tampilkan kode QR", + "description": "Context-menu action to show an account's QR code" + }, + "pin_to_top": { + "message": "Sematkan ke atas", + "description": "Context-menu action to pin an account to the top" + }, + "unpin": { + "message": "Lepas sematan", + "description": "Context-menu action to unpin an account" + }, + "vault_locked": { + "message": "Brankas terkunci", + "description": "Title on the unlock screen" + }, + "unlock": { + "message": "Buka kunci", + "description": "Unlock button" + }, + "qr_transfer_title": { + "message": "Transfer akun ini", + "description": "QR sheet caption title" + }, + "qr_transfer_desc": { + "message": "Pindai kode ini dengan OTPilot di perangkat lain untuk memindahkan akun ini — data tidak pernah meninggalkan perangkat Anda.", + "description": "QR sheet caption body" + }, + "backup_on_device": { + "message": "Di perangkat ini", + "description": "Backup section: local" + }, + "backup_cloud_sync": { + "message": "Sinkronisasi cloud", + "description": "Backup section: cloud" + }, + "backup_connected": { + "message": "Terhubung", + "description": "Cloud provider connected status" + }, + "backup_not_connected": { + "message": "Tidak terhubung", + "description": "Cloud provider not-connected status" + }, + "showing_codes_for": { + "message": "Menampilkan kode untuk", + "description": "Smart-filter banner prefix, followed by the site domain" + }, + "other_accounts": { + "message": "Akun lainnya", + "description": "Divider above non-matching accounts in the filter view" + }, + "filter_to_site": { + "message": "Filter ke", + "description": "Banner prefix to re-apply the site filter, followed by the domain" + }, + "onboarding_title": { + "message": "Kode yang selalu menyertaimu", + "description": "Onboarding headline" + }, + "onboarding_subtitle": { + "message": "Hasilkan kode dua langkah yang aman untuk setiap akun — terenkripsi, offline, dan selalu siap disalin dengan satu ketukan.", + "description": "Onboarding subtitle" + }, + "onboarding_feature_offline": { + "message": "Berfungsi sepenuhnya offline — tidak ada data yang meninggalkan perangkat Anda", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_encrypted": { + "message": "Brankas terenkripsi dengan kunci kata sandi opsional", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_scan": { + "message": "Pindai kode QR atau impor cadangan yang sudah ada", + "description": "Onboarding feature bullet" + }, + "onboarding_get_started": { + "message": "Mulai", + "description": "Onboarding primary button" + }, + "onboarding_import": { + "message": "Saya punya cadangan untuk diimpor", + "description": "Onboarding secondary button" + }, "theme_light": { "message": "Terang", "description": "Light theme" @@ -313,26 +429,18 @@ "message": "Gelap", "description": "Dark theme" }, - "theme_simple": { - "message": "Sederhana", - "description": "Simple theme" + "theme_auto": { + "message": "Otomatis", + "description": "Follow the system light/dark setting" }, "theme_compact": { - "message": "Sederhana", + "message": "Ringkas", "description": "Compact theme" }, "theme_high_contrast": { "message": "Kontras Tinggi", "description": "High Contrast theme" }, - "theme_flat": { - "message": "Rata", - "description": "Flat theme" - }, - "storage_sync_info": { - "message": "Otomatis mencadangkan data kamu ke layanan penyimpanan pihak ke-3.", - "description": "3rd party backup info" - }, "browser_sync": { "message": "Sinkronisasi browser", "description": "Storage location" @@ -342,18 +450,18 @@ "description": "Sign in to 3rd party storage services" }, "sign_in_business": { - "message": "Masuk (Business)", + "message": "Masuk (Bisnis)", "description": "Sign in to 3rd party storage services" }, "onedrive_business_perms": { - "message": "Mengapa bisnis akun membutuhkan lebih banyak permission?" + "message": "Mengapa akun bisnis membutuhkan lebih banyak izin?" }, "log_out": { "message": "Keluar", "description": "Sign out of 3rd party storage services" }, "token_revoked": { - "message": "Ada error saat menghubungkan ke akun $SERVICE$ kamu, silahkan masuk lagi.", + "message": "Terjadi masalah saat menghubungkan ke akun $SERVICE$ Anda, silakan masuk kembali.", "description": "Error authenticating to backup service. $SERVICE$ will be replaced with a proper noun (E.g.: 'Google Drive' or 'Dropbox')", "placeholders": { "service": { @@ -362,10 +470,6 @@ } } }, - "otp_unsupported_warn": { - "message": "Anda memiliki satu atau lebih akun Steam atau Blizzard. Cadangan yang tidak terenkripsi tidak akan menggunakan format cadangan terstandarisasi.", - "description": "Warning if using account that is not supported by standard backup format." - }, "otp_backup_inform": { "message": "Anda dapat mengimpor cadangan dari aplikasi lain.", "description": "Info text on import page" @@ -378,13 +482,13 @@ "message": "Memuat..." }, "autolock": { - "message": "Mengunci aplikasi" + "message": "Kunci setelah" }, "minutes": { "message": "menit" }, "advanced": { - "message": "Tingkat lanjut" + "message": "Lanjutan" }, "period": { "message": "Periode" @@ -402,43 +506,40 @@ "message": "Algoritma" }, "smart_filter": { - "message": "Filter pintar" + "message": "Filter Cerdas" }, "backup": { "message": "Cadangan" }, - "backup_file_info": { - "message": "Cadangkan data Anda ke file." - }, "password_policy_default_hint": { - "message": "Kata sandi tidak memenuhi persyaratan keamanan organisasi anda. Hubungi administrator untuk informasi lebih lanjut." + "message": "Kata sandi tidak memenuhi persyaratan keamanan organisasi Anda. Hubungi administrator untuk informasi lebih lanjut." }, "advisor": { - "message": "Penasehat" + "message": "Penasihat" }, "advisor_insight_password_not_set": { - "message": "Masukkan kata sandi untuk melindungi data anda." + "message": "Masukkan kata sandi untuk melindungi data Anda." }, "advisor_insight_auto_lock_not_set": { - "message": "Aktifkan auto-lock untuk melindungi data anda." + "message": "Aktifkan auto-lock untuk melindungi data Anda." }, "advisor_insight_browser_sync_not_enabled": { "message": "Sinkronisasi browser nonaktif. Mengaktifkannya membuat akun sinkron di semua browser." }, "advisor_insight_auto_fill_not_enabled": { - "message": "Autofill dapat diaktifkan untuk mengisi kode secara otomatis ke situs web." + "message": "Isi otomatis dapat diaktifkan untuk mengisi kode secara otomatis ke situs web." }, "advisor_insight_smart_filter_not_enabled": { "message": "Mengaktifkan filter cerdas memungkinkan akses cepat ke akun." }, "show_all_insights": { - "message": "Melihat semua pengumuman." + "message": "Lihat semua wawasan." }, "no_insight_available": { - "message": "Pengumuman kosong, semua normal!" + "message": "Tidak ada wawasan ditemukan, semuanya terlihat baik!" }, "danger": { - "message": "Perhatian" + "message": "Bahaya" }, "warning": { "message": "Peringatan" @@ -462,16 +563,16 @@ "message": "Perizinan" }, "permission_revoke": { - "message": "Revoke" + "message": "Cabut" }, "permission_show_required_permissions": { - "message": "Menampilkan ijin yang tidak bisa dibatalkan" + "message": "Tampilkan izin yang tidak dapat dicabut" }, "permission_required": { - "message": "Tindakan yang membutuhkan izin dan tidak bisa dibatalkan." + "message": "Ini adalah izin wajib dan tidak dapat dicabut." }, "permission_active_tab": { - "message": "Akses yang diarahkan ke tab terkait untuk memindai kode QR." + "message": "Akses ke tab saat ini untuk memindai kode QR." }, "permission_storage": { "message": "Akses ke penyimpanan peramban untuk menyimpan data akun." @@ -489,7 +590,7 @@ "message": "Memberikan akses write-only ke clipboard untuk menyalin kode ke clipboard saat Anda meng-klik akun." }, "permission_context_menus": { - "message": "Menambahkan Authenticator ke menu konteks." + "message": "Menambahkan OTPilot ke menu konteks." }, "permission_sync_clock": { "message": "Mengizinkan sinkronisasi jam dengan Google." @@ -498,27 +599,19 @@ "message": "Izinkan pencadangan ke Dropbox." }, "permission_dropbox_cannot_revoke": { - "message": "Anda harus me-non-aktifkan cadangan Dropbox terlebih dahulu." - }, - "permission_drive": { - "message": "Izinkan pencadangan ke Google Drive." - }, - "permission_drive_cannot_revoke": { - "message": "Anda harus me-non-aktifkan cadangan Google Drive terlebih dahulu." - }, - "permission_onedrive": { - "message": "Izinkan pencadangan ke OneDrive." - }, - "permission_onedrive_cannot_revoke": { - "message": "Anda harus me-non-aktifkan cadangan OneDrive terlebih dahulu." + "message": "Anda harus menonaktifkan cadangan Dropbox terlebih dahulu." }, "permission_unknown_permission": { "message": "Izin tidak diketahui. Jika melihat pesan ini, kirimkan laporan kesalahan." }, "phrase_wrong": { - "message": "Password incorrect" + "message": "Kata sandi salah" }, "activate_auto_filter": { - "message": "Warning: Smart filter loosely matches the domain name to an account. Always verify that you are on the correct website before entering a code!" + "message": "Peringatan: Filter cerdas mencocokkan nama domain dengan akun secara longgar. Selalu pastikan Anda berada di situs web yang benar sebelum memasukkan kode!" + }, + "backup_unavailable": { + "message": "Belum tersedia", + "description": "Shown on a cloud provider that is not implemented yet." } } diff --git a/_locales/it/messages.json b/_locales/it/messages.json index 2ae338f2c..da38e2559 100644 --- a/_locales/it/messages.json +++ b/_locales/it/messages.json @@ -1,14 +1,22 @@ { "extName": { - "message": "Authenticator", + "message": "OTPilot Authenticator", "description": "Extension Name." }, "extShortName": { - "message": "Authenticator", + "message": "OTPilot", "description": "Extension Short Name." }, + "host": { + "message": "Sito web", + "description": "Bound website host used to restrict autofill to a matching page." + }, + "backup_requires_password": { + "message": "Imposta prima una password per usare il backup cloud, così i tuoi segreti vengono criptati prima di lasciare questo dispositivo.", + "description": "Shown in the cloud backup section when no master password is set." + }, "extDesc": { - "message": "Authenticator genera codici di verifica a due fattori nel tuo browser.", + "message": "OTPilot Authenticator genera codici di verifica a due fattori (2FA), con backup cloud criptato, autocompletamento basato sul sito e consigli di sicurezza.", "description": "Extension Description." }, "added": { @@ -23,6 +31,10 @@ "message": "Chiave segreta dell'account non valida", "description": "Secret Error." }, + "errorissuer": { + "message": "L'emittente non può contenere \"::\"", + "description": "Issuer Error." + }, "add_code": { "message": "Aggiungi account", "description": "Add account." @@ -43,10 +55,6 @@ "message": "Alcuni dati dell'account non sono stati importati con successo.", "description": "Some migration data is broken." }, - "close": { - "message": "Chiudi", - "description": "Close." - }, "ok": { "message": "Ok", "description": "OK." @@ -68,7 +76,7 @@ "description": "Account Name." }, "issuer": { - "message": "Emittente del codice", + "message": "Emittente", "description": "Issuer." }, "secret": { @@ -99,10 +107,6 @@ "message": "Password corrente", "description": "Current Passphrase." }, - "new_phrase": { - "message": "Nuova password", - "description": "New Passphrase." - }, "phrase": { "message": "Password", "description": "Passphrase." @@ -120,19 +124,15 @@ "description": "Message that user is required to acknowledge before clearing all data." }, "delete_all": { - "message": "Resetta Autenticatore" + "message": "Reimposta OTPilot" }, "delete_all_warning": { - "message": "Questo eliminerà tutti i tuoi dati e ripristinerà completamente l'Authenticator. Non potrai recuperare alcun dato eliminato! Dovresti considerare di salvare un backup, prima di ripristinare l'Authenticator." + "message": "Questo eliminerà tutti i tuoi dati e ripristinerà completamente OTPilot. Non potrai recuperare alcun dato eliminato! Dovresti considerare di salvare un backup prima di ripristinare OTPilot." }, "security_warning": { - "message": "Questa password verrà usata per cifrare i tuoi account. Se dimentichi la password non ti potrà aiutare nessuno.", + "message": "Questa password verrà usata per criptare i tuoi account. Se dimentichi la password non ti potrà aiutare nessuno.", "description": "Passphrase Warning." }, - "update": { - "message": "Aggiorna", - "description": "Update." - }, "phrase_incorrect": { "message": "Non è possibile aggiungere un nuovo account fino a quando tutti gli account non saranno decrittografati. Si prega di inserire la password corretta prima di continuare.", "description": "Passphrase Incorrect." @@ -157,10 +157,6 @@ "message": "Feedback", "description": "Feedback." }, - "translate": { - "message": "Traduci", - "description": "Translate." - }, "source": { "message": "Codice Sorgente", "description": "Source Code." @@ -173,12 +169,8 @@ "message": "Sincronizzazione orologio con Google", "description": "Sync Clock" }, - "remember_phrase": { - "message": "Ricorda la Password", - "description": "Remember Passphrase" - }, "clock_too_far_off": { - "message": "Attenzione! C'è una differenza troppo grande con l'orologio locale, risolvere prima di continuare.", + "message": "Attenzione! Il tuo orologio locale è troppo disallineato, correggilo prima di continuare.", "description": "Local Time is Too Far Off" }, "remind_backup": { @@ -210,7 +202,7 @@ "description": "Scale" }, "export_info": { - "message": "Attenzione: tutti i backup sono non criptati. Vuoi aggiungere un account a un'altra app? Passa il mouse sopra la parte superiore destra di qualsiasi account e premi il pulsante nascosto.", + "message": "Attenzione: tutti i backup non sono criptati. Vuoi aggiungere un account a un'altra app? Passa il mouse sopra la parte superiore destra di qualsiasi account e premi il pulsante nascosto.", "description": "Export menu info text" }, "download_backup": { @@ -234,13 +226,45 @@ "description": "Import qr images. Shown as add account method." }, "import_backup_code": { - "message": "Importa Backup testuale", + "message": "Importa URI OTPAuth", "description": "Import backup code." }, "import_otp_urls": { - "message": "Importa URL OTP", + "message": "Importa URI OTPAuth", "description": "Import OTP URLs. Shown as add account method." }, + "import_drop_file": { + "message": "Trascina qui un file di backup o fai clic per sfogliare", + "description": "File import dropzone title" + }, + "import_file_accepts": { + "message": "Accetta file .json e .txt", + "description": "File import dropzone hint" + }, + "import_choose_file": { + "message": "Scegli file", + "description": "File import dropzone button" + }, + "import_encrypted_note": { + "message": "Se il file è criptato, ti verrà chiesta la password dopo averlo selezionato.", + "description": "File import note about encrypted backups" + }, + "import_enc_required": { + "message": "Criptato — password richiesta", + "description": "Badge on a picked encrypted backup file" + }, + "import_pass_placeholder": { + "message": "Inserisci la password del file", + "description": "Passphrase input placeholder for encrypted import" + }, + "import_pass_hint": { + "message": "Questa è la password che hai impostato quando hai creato il backup.", + "description": "Hint under the import passphrase field" + }, + "import_decrypt": { + "message": "Decripta e importa", + "description": "Button to decrypt and import an encrypted backup" + }, "import_backup_qr_partly_failed": { "message": "Importazione riuscita, ma alcuni codici QR non possono essere riconosciuti.", "description": "Import successful, but some QR image cannot be recognized." @@ -297,14 +321,106 @@ "message": "Usa Autocompletamento", "description": "Use Autofill" }, - "use_high_contrast": { - "message": "Usa Alto Contrasto", - "description": "Use High Contrast" - }, "theme": { "message": "Tema", "description": "Theme" }, + "settings_appearance": { + "message": "Aspetto", + "description": "Settings section: appearance" + }, + "settings_general": { + "message": "Generale", + "description": "Settings section: general" + }, + "edit_accounts": { + "message": "Modifica account", + "description": "Header title while in edit mode" + }, + "show_qr": { + "message": "Mostra codice QR", + "description": "Context-menu action to show an account's QR code" + }, + "pin_to_top": { + "message": "Fissa in cima", + "description": "Context-menu action to pin an account to the top" + }, + "unpin": { + "message": "Rimuovi fissaggio", + "description": "Context-menu action to unpin an account" + }, + "vault_locked": { + "message": "Archivio bloccato", + "description": "Title on the unlock screen" + }, + "unlock": { + "message": "Sblocca", + "description": "Unlock button" + }, + "qr_transfer_title": { + "message": "Trasferisci questo account", + "description": "QR sheet caption title" + }, + "qr_transfer_desc": { + "message": "Scansiona questo codice con OTPilot su un altro dispositivo per trasferire questo account: non lascia mai i tuoi dispositivi.", + "description": "QR sheet caption body" + }, + "backup_on_device": { + "message": "Su questo dispositivo", + "description": "Backup section: local" + }, + "backup_cloud_sync": { + "message": "Sincronizzazione cloud", + "description": "Backup section: cloud" + }, + "backup_connected": { + "message": "Connesso", + "description": "Cloud provider connected status" + }, + "backup_not_connected": { + "message": "Non connesso", + "description": "Cloud provider not-connected status" + }, + "showing_codes_for": { + "message": "Codici visualizzati per", + "description": "Smart-filter banner prefix, followed by the site domain" + }, + "other_accounts": { + "message": "Altri account", + "description": "Divider above non-matching accounts in the filter view" + }, + "filter_to_site": { + "message": "Filtra per", + "description": "Banner prefix to re-apply the site filter, followed by the domain" + }, + "onboarding_title": { + "message": "Codici che ti accompagnano ovunque", + "description": "Onboarding headline" + }, + "onboarding_subtitle": { + "message": "Genera codici a due fattori sicuri per ogni account — criptati, offline e sempre a un tocco dalla copia.", + "description": "Onboarding subtitle" + }, + "onboarding_feature_offline": { + "message": "Funziona completamente offline — nulla lascia il tuo dispositivo", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_encrypted": { + "message": "Archivio criptato con blocco tramite password opzionale", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_scan": { + "message": "Scansiona un codice QR o importa un backup esistente", + "description": "Onboarding feature bullet" + }, + "onboarding_get_started": { + "message": "Inizia", + "description": "Onboarding primary button" + }, + "onboarding_import": { + "message": "Ho un backup da importare", + "description": "Onboarding secondary button" + }, "theme_light": { "message": "Chiaro", "description": "Light theme" @@ -313,9 +429,9 @@ "message": "Scuro", "description": "Dark theme" }, - "theme_simple": { - "message": "Semplice", - "description": "Simple theme" + "theme_auto": { + "message": "Automatico", + "description": "Follow the system light/dark setting" }, "theme_compact": { "message": "Compatto", @@ -325,14 +441,6 @@ "message": "Contrasto Elevato", "description": "High Contrast theme" }, - "theme_flat": { - "message": "Flat", - "description": "Flat theme" - }, - "storage_sync_info": { - "message": "Backup automatico dei tuoi dati su servizi di terze parti.", - "description": "3rd party backup info" - }, "browser_sync": { "message": "Sincronizzazione Browser", "description": "Storage location" @@ -346,7 +454,7 @@ "description": "Sign in to 3rd party storage services" }, "onedrive_business_perms": { - "message": "Perché i profili business richiedono più permessi?" + "message": "Perché gli account business richiedono più permessi?" }, "log_out": { "message": "Disconnetti", @@ -362,10 +470,6 @@ } } }, - "otp_unsupported_warn": { - "message": "Hai uno o più account di Steam o Blizzard. I backup non cifrati non utilizzeranno il formato di backup standard.", - "description": "Warning if using account that is not supported by standard backup format." - }, "otp_backup_inform": { "message": "Puoi importare i backup da altre applicazioni.", "description": "Info text on import page" @@ -396,7 +500,7 @@ "message": "Non valido" }, "digits": { - "message": "Caratteri" + "message": "Cifre" }, "algorithm": { "message": "Algoritmo" @@ -407,9 +511,6 @@ "backup": { "message": "Backup" }, - "backup_file_info": { - "message": "Fai il backup dei tuoi dati su un file." - }, "password_policy_default_hint": { "message": "La tua password non soddisfa i requisiti di sicurezza della tua organizzazione. Contatta l'amministratore per maggiori informazioni." }, @@ -423,19 +524,19 @@ "message": "Abilita il blocco automatico per proteggere i tuoi dati." }, "advisor_insight_browser_sync_not_enabled": { - "message": "La sincronizzazione del browser è disabilitata. Abilitarla consente ai profili di esser sincronizzati tra i browser." + "message": "La sincronizzazione del browser è disabilitata. Abilitarla consente di sincronizzare gli account tra i browser." }, "advisor_insight_auto_fill_not_enabled": { - "message": "Il riempimento automatico è abilitabile per compilare automaticamente i codici nei siti web." + "message": "Puoi abilitare l'autocompletamento per inserire automaticamente i codici nei siti web." }, "advisor_insight_smart_filter_not_enabled": { - "message": "Abilitare il filtro smart consente l'accesso rapido ai profili." + "message": "Abilitare il filtro intelligente consente l'accesso rapido agli account." }, "show_all_insights": { - "message": "Mostra tutti i dettagli." + "message": "Mostra tutti i suggerimenti." }, "no_insight_available": { - "message": "Nessun dettaglio trovato, sembra andare tutto bene!" + "message": "Nessun suggerimento trovato, sembra andare tutto bene!" }, "danger": { "message": "Pericolo" @@ -474,22 +575,22 @@ "message": "Accedi alla scheda corrente per scansionare i codici QR." }, "permission_storage": { - "message": "Accedi all'archiviazione del browser per memorizzare i dati del profilo." + "message": "Accedi all'archiviazione del browser per memorizzare i dati dell'account." }, "permission_identity": { "message": "Consenti l'accesso a servizi d'archiviazione di terze parti." }, "permission_alarms": { - "message": "Allows auto-lock to work." + "message": "Consenti il funzionamento del blocco automatico." }, "permission_scripting": { - "message": "Inject scripts into he current tab to scan QR codes and allow auto-fill to work." + "message": "Inserisci script nella scheda corrente per scansionare i codici QR e consentire il funzionamento dell'autocompletamento." }, "permission_clipboard_write": { - "message": "Concedi l'accesso di sola scrittura agli appunti per copiarvi i codici quando clicchi sul profilo." + "message": "Concedi l'accesso di sola scrittura agli appunti per copiarvi i codici quando clicchi sull'account." }, "permission_context_menus": { - "message": "Aggiungi Authenticatori al menu contestuale." + "message": "Aggiungi OTPilot al menu contestuale." }, "permission_sync_clock": { "message": "Consenti la sincronizzazione dell'orario con Google." @@ -500,25 +601,17 @@ "permission_dropbox_cannot_revoke": { "message": "Devi prima disabilitare il backup di Dropbox." }, - "permission_drive": { - "message": "Consenti il backup a Google Drive." - }, - "permission_drive_cannot_revoke": { - "message": "Devi prima disabilitare il backup di Google Drive." - }, - "permission_onedrive": { - "message": "Consenti il backup su OneDrive." - }, - "permission_onedrive_cannot_revoke": { - "message": "Devi prima disabilitare il backup su OneDrive." - }, "permission_unknown_permission": { "message": "Autorizzazione sconosciuta. Se vedi questo messaggio, sei pregato di inviare una segnalazione di bug." }, "phrase_wrong": { - "message": "Password incorrect" + "message": "Password errata" }, "activate_auto_filter": { - "message": "Warning: Smart filter loosely matches the domain name to an account. Always verify that you are on the correct website before entering a code!" + "message": "Attenzione: il filtro intelligente associa il dominio a un account in modo approssimativo. Verifica sempre di trovarti sul sito corretto prima di inserire un codice!" + }, + "backup_unavailable": { + "message": "Non ancora disponibile", + "description": "Shown on a cloud provider that is not implemented yet." } } diff --git a/_locales/ja/messages.json b/_locales/ja/messages.json index aa2b9c707..be21c302c 100644 --- a/_locales/ja/messages.json +++ b/_locales/ja/messages.json @@ -1,14 +1,14 @@ { "extName": { - "message": "Authenticator", + "message": "OTPilot Authenticator", "description": "Extension Name." }, "extShortName": { - "message": "Authenticator", + "message": "OTPilot", "description": "Extension Short Name." }, "extDesc": { - "message": "Authenticator はお使いのブラウザーで2段階認証コードを生成します。", + "message": "OTPilot Authenticatorは、暗号化クラウドバックアップ、サイトに応じた自動入力、セキュリティアドバイスを備えた2段階認証(2FA)コード生成アプリです。", "description": "Extension Description." }, "added": { @@ -16,13 +16,17 @@ "description": "Added Account." }, "errorqr": { - "message": "QR コードが認識できません。", + "message": "QRコードが認識できません。", "description": "QR Error." }, "errorsecret": { - "message": "無効なアカウントのシークレットキー", + "message": "無効なアカウントのシークレット", "description": "Secret Error." }, + "errorissuer": { + "message": "発行者に「::」を含めることはできません", + "description": "Issuer Error." + }, "add_code": { "message": "アカウントを追加", "description": "Add account." @@ -43,10 +47,6 @@ "message": "いくつかのアカウントデータは正常にインポートされませんでした。", "description": "Some migration data is broken." }, - "close": { - "message": "閉じる", - "description": "Close." - }, "ok": { "message": "OK", "description": "OK." @@ -99,10 +99,6 @@ "message": "現在のパスワード", "description": "Current Passphrase." }, - "new_phrase": { - "message": "新しいパスワード", - "description": "New Passphrase." - }, "phrase": { "message": "パスワード", "description": "Passphrase." @@ -116,23 +112,19 @@ "description": "Remove entry confirmation" }, "confirm_delete_all": { - "message": "私はすべてのデータが削除されて復旧できないことを承知しています。", + "message": "すべてのデータが完全に削除され、復元できないことを理解しています。", "description": "Message that user is required to acknowledge before clearing all data." }, "delete_all": { - "message": "認証システムをリセット" + "message": "OTPilotをリセット" }, "delete_all_warning": { - "message": "これにより、すべてのデータが削除され、認証システムが完全にリセットされます。 削除されたデータを復元することはできません。 認証システムをリセットする前にバックアップを保存することをお勧めします。" + "message": "これにより、すべてのデータが削除され、OTPilotが完全にリセットされます。削除されたデータを復元することはできません。OTPilotをリセットする前にバックアップを保存することをお勧めします。" }, "security_warning": { - "message": "このパスワードであなたのアカウントを暗号化します。パスワードを紛失すると復旧できなくなりますのでご注意ください。", + "message": "このパスワードはアカウントの暗号化に使用されます。パスワードを忘れると、誰にも復旧できません。", "description": "Passphrase Warning." }, - "update": { - "message": "更新", - "description": "Update." - }, "phrase_incorrect": { "message": "すべてのアカウントが復号化されるまで、新しいアカウントを追加することはできません。続行する前に正しいパスワードを入力してください。", "description": "Passphrase Incorrect." @@ -146,7 +138,7 @@ "description": "Passphrase Not Match." }, "encrypted": { - "message": "暗号化", + "message": "暗号化済み", "description": "Encrypted." }, "copied": { @@ -154,15 +146,11 @@ "description": "Copied." }, "feedback": { - "message": "ご意見", + "message": "フィードバック", "description": "Feedback." }, - "translate": { - "message": "翻訳", - "description": "Translate." - }, "source": { - "message": "ソース コード", + "message": "ソースコード", "description": "Source Code." }, "passphrase_info": { @@ -170,13 +158,9 @@ "description": "Passphrase Info" }, "sync_clock": { - "message": "Google と時刻を同期\n", + "message": "Google と時刻を同期", "description": "Sync Clock" }, - "remember_phrase": { - "message": "パスワードを記憶させる", - "description": "Remember Passphrase" - }, "clock_too_far_off": { "message": "危険!お使いの環境の日時が実際の日時と離れすぎています。続ける前にまずは修正してください。", "description": "Local Time is Too Far Off" @@ -190,15 +174,15 @@ "description": "Capture Failed" }, "capture_local_file_failed": { - "message": "ローカルファイルからQRコードをスキャンしようとしていますか? 代わりにQRイメージバックアップをインポートしてください。", + "message": "ローカルファイルからQRコードをスキャンしようとしていますか? 代わりに「QR画像のバックアップをインポート」をご利用ください。", "description": "Import QR image backup instead of scan local image" }, "based_on_time": { - "message": "タイム ベース", + "message": "タイムベース", "description": "Time Based" }, "based_on_counter": { - "message": "カウンター ベース", + "message": "カウンターベース", "description": "Counter Based" }, "resize_popup_page": { @@ -226,19 +210,19 @@ "description": "Import backup file." }, "import_backup_qr": { - "message": "QR 画像のバックアップをインポート", + "message": "QR画像のバックアップをインポート", "description": "Import qr image backup." }, "import_qr_images": { - "message": "QRコード画像をインポート", + "message": "QR画像をインポート", "description": "Import qr images. Shown as add account method." }, "import_backup_code": { - "message": "テキストのバックアップのインポート", + "message": "OTPAuth URIをインポート", "description": "Import backup code." }, "import_otp_urls": { - "message": "OTP URLをインポート", + "message": "OTPAuth URIをインポート", "description": "Import OTP URLs. Shown as add account method." }, "import_backup_qr_partly_failed": { @@ -246,15 +230,15 @@ "description": "Import successful, but some QR image cannot be recognized." }, "import_backup_qr_in_batches": { - "message": "バッチでバックアップをインポートするファイルを複数選択できます。", + "message": "複数のファイルを選択して、バックアップを一括インポートできます。", "description": "You can select multiple image files to import backup in batches." }, "show_all_entries": { - "message": "すべての登録を表示", + "message": "すべての項目を表示", "description": "Show all entries." }, "dropbox_risk": { - "message": "警告: バックアップは暗号化されていません。お気をつけください。", + "message": "警告: バックアップは暗号化されていません。自己責任でご使用ください。", "description": "Backup risk warning." }, "import_error_password": { @@ -297,10 +281,6 @@ "message": "自動入力を使用する", "description": "Use Autofill" }, - "use_high_contrast": { - "message": "高コントラストにする", - "description": "Use High Contrast" - }, "theme": { "message": "テーマ", "description": "Theme" @@ -313,10 +293,6 @@ "message": "ダーク", "description": "Dark theme" }, - "theme_simple": { - "message": "シンプル", - "description": "Simple theme" - }, "theme_compact": { "message": "コンパクト", "description": "Compact theme" @@ -325,16 +301,8 @@ "message": "高コントラスト", "description": "High Contrast theme" }, - "theme_flat": { - "message": "フラット", - "description": "Flat theme" - }, - "storage_sync_info": { - "message": "サードパーティのストレージサービスへ自動的にデータをバックアップします。", - "description": "3rd party backup info" - }, "browser_sync": { - "message": "ブラウザ間で同期", + "message": "ブラウザ同期", "description": "Storage location" }, "sign_in": { @@ -342,11 +310,11 @@ "description": "Sign in to 3rd party storage services" }, "sign_in_business": { - "message": "Sign in (Business)", + "message": "サインイン(ビジネス)", "description": "Sign in to 3rd party storage services" }, "onedrive_business_perms": { - "message": "なぜビジネスアカウントにはより多くの権限が必要なのですか?" + "message": "なぜビジネスアカウントにはより多くの権限が必要なのですか?" }, "log_out": { "message": "ログアウト", @@ -362,10 +330,6 @@ } } }, - "otp_unsupported_warn": { - "message": "使用できないアカウントが含まれています。暗号化されていないバックアップでは、標準のバックアップ形式を使用できません。", - "description": "Warning if using account that is not supported by standard backup format." - }, "otp_backup_inform": { "message": "他のアプリのバックアップをインポートできます。", "description": "Info text on import page" @@ -378,13 +342,13 @@ "message": "読み込み中..." }, "autolock": { - "message": "ロック時間" + "message": "自動ロックまでの時間" }, "minutes": { "message": "分" }, "advanced": { - "message": "その他" + "message": "詳細設定" }, "period": { "message": "期間" @@ -407,9 +371,6 @@ "backup": { "message": "バックアップ" }, - "backup_file_info": { - "message": "データをファイルにバックアップします。" - }, "password_policy_default_hint": { "message": "パスワードが組織のセキュリティ要件を満たしていません。詳しくは管理者にお問い合わせください。" }, @@ -417,19 +378,19 @@ "message": "アドバイザー" }, "advisor_insight_password_not_set": { - "message": "データを保護するためにパスワードを設定します。" + "message": "データを保護するために、パスワードを設定しましょう。" }, "advisor_insight_auto_lock_not_set": { - "message": "自動ロックを有効にしてデータを保護します。" + "message": "データを保護するために、自動ロックを有効にしましょう。" }, "advisor_insight_browser_sync_not_enabled": { - "message": "ブラウザの同期が無効になっています。アカウントを有効にするとブラウザ間で同期することができます。" + "message": "ブラウザ同期が無効になっています。有効にすると、アカウントをブラウザ間で同期できるようになります。" }, "advisor_insight_auto_fill_not_enabled": { "message": "自動入力を有効にすると、ウェブサイトで自動的にコードを入力できます。" }, "advisor_insight_smart_filter_not_enabled": { - "message": "スマートフィルタを有効にすると、アカウントへ素早くアクセスできます。" + "message": "スマートフィルターを有効にすると、アカウントへ素早くアクセスできます。" }, "show_all_insights": { "message": "すべてのインサイトを表示します。" @@ -441,16 +402,16 @@ "message": "危険" }, "warning": { - "message": "注意" + "message": "警告" }, "info": { - "message": "お知らせ" + "message": "情報" }, "dismiss": { - "message": "消去する" + "message": "閉じる" }, "learn_more": { - "message": "続きを読む" + "message": "詳細" }, "enable_context_menu": { "message": "コンテキストメニューに追加" @@ -480,45 +441,177 @@ "message": "サードパーティのストレージサービスへのサインインを許可します。" }, "permission_alarms": { - "message": "機能を有効化するには、画面の自動ロックを有効にします。" + "message": "自動ロック機能を動作させるために使用します。" }, "permission_scripting": { - "message": "現在のタブにスクリプトを挿入してQRコードをスキャンし、自動入力がが機能するようにする。" + "message": "現在のタブにスクリプトを挿入してQRコードをスキャンし、自動入力が機能するようにします。" }, "permission_clipboard_write": { "message": "アカウントをクリックしたときにクリップボードへの書き込み専用の権限を許可します。" }, "permission_context_menus": { - "message": "コンテキストメニューに認証システムを追加します。" + "message": "コンテキストメニューにOTPilotを追加します。" }, "permission_sync_clock": { "message": "Google との時刻の同期を許可します。" }, "permission_dropbox": { - "message": "Dropboxへバックアップを行います。" + "message": "Dropboxへのバックアップを許可します。" }, "permission_dropbox_cannot_revoke": { "message": "最初にDropboxのバックアップを無効にする必要があります。" }, - "permission_drive": { - "message": "Google ドライブへバックアップを行います。" - }, - "permission_drive_cannot_revoke": { - "message": "まず、Google ドライブのバックアップを無効にする必要があります。" - }, - "permission_onedrive": { - "message": "OneDriveへバックアップを行います。" - }, - "permission_onedrive_cannot_revoke": { - "message": "最初にOneDriveへのバックアップを無効にする必要があります。" - }, "permission_unknown_permission": { "message": "不明な権限です。このメッセージが表示される場合はバグレポートを送信してください。" }, "phrase_wrong": { - "message": "Password incorrect" + "message": "パスワードが違います。" }, "activate_auto_filter": { - "message": "警告:スマートフィルタはアカウントのドメイン名とほぼ一致します。常にあなたが正しいウェブサイトにいることを確認してください!" + "message": "警告:スマートフィルターは、ドメイン名のゆるやかな一致でアカウントを判定します。コードを入力する前に、必ず正しいウェブサイトにいることを確認してください!" + }, + "backup_cloud_sync": { + "message": "クラウド同期", + "description": "Backup section: cloud" + }, + "backup_connected": { + "message": "接続済み", + "description": "Cloud provider connected status" + }, + "backup_not_connected": { + "message": "未接続", + "description": "Cloud provider not-connected status" + }, + "backup_on_device": { + "message": "この端末", + "description": "Backup section: local" + }, + "backup_requires_password": { + "message": "クラウドバックアップを使うには、先にパスワードを設定してください。シークレットはこの端末を離れる前に暗号化されます。", + "description": "Shown in the cloud backup section when no master password is set." + }, + "edit_accounts": { + "message": "アカウントを編集", + "description": "Header title while in edit mode" + }, + "filter_to_site": { + "message": "絞り込み:", + "description": "Banner prefix to re-apply the site filter, followed by the domain" + }, + "host": { + "message": "ウェブサイト", + "description": "Bound website host used to restrict autofill to a matching page." + }, + "import_choose_file": { + "message": "ファイルを選択", + "description": "File import dropzone button" + }, + "import_decrypt": { + "message": "復号してインポート", + "description": "Button to decrypt and import an encrypted backup" + }, + "import_drop_file": { + "message": "バックアップファイルをドロップ、またはクリックして選択", + "description": "File import dropzone title" + }, + "import_enc_required": { + "message": "暗号化済み — パスフレーズが必要", + "description": "Badge on a picked encrypted backup file" + }, + "import_encrypted_note": { + "message": "ファイルが暗号化されている場合、選択後にパスフレーズの入力を求められます。", + "description": "File import note about encrypted backups" + }, + "import_file_accepts": { + "message": ".json と .txt のエクスポートに対応", + "description": "File import dropzone hint" + }, + "import_pass_hint": { + "message": "バックアップ作成時に設定したパスワードです。", + "description": "Hint under the import passphrase field" + }, + "import_pass_placeholder": { + "message": "ファイルのパスフレーズを入力", + "description": "Passphrase input placeholder for encrypted import" + }, + "onboarding_feature_encrypted": { + "message": "パスワードロック(任意)付きの暗号化保管庫", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_offline": { + "message": "完全オフラインで動作 — データは端末から出ません", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_scan": { + "message": "QRコードをスキャン、または既存のバックアップをインポート", + "description": "Onboarding feature bullet" + }, + "onboarding_get_started": { + "message": "はじめる", + "description": "Onboarding primary button" + }, + "onboarding_import": { + "message": "インポートするバックアップがあります", + "description": "Onboarding secondary button" + }, + "onboarding_subtitle": { + "message": "すべてのアカウントに安全な2段階認証コードを生成 — 暗号化、オフライン、ワンタップでコピー。", + "description": "Onboarding subtitle" + }, + "onboarding_title": { + "message": "持ち歩ける認証コード", + "description": "Onboarding headline" + }, + "other_accounts": { + "message": "その他のアカウント", + "description": "Divider above non-matching accounts in the filter view" + }, + "pin_to_top": { + "message": "上部に固定", + "description": "Context-menu action to pin an account to the top" + }, + "qr_transfer_desc": { + "message": "別の端末の OTPilot でこのコードをスキャンすると、このアカウントを移行できます — データが端末の外に出ることはありません。", + "description": "QR sheet caption body" + }, + "qr_transfer_title": { + "message": "このアカウントを移行", + "description": "QR sheet caption title" + }, + "settings_appearance": { + "message": "外観", + "description": "Settings section: appearance" + }, + "settings_general": { + "message": "一般", + "description": "Settings section: general" + }, + "show_qr": { + "message": "QRコードを表示", + "description": "Context-menu action to show an account's QR code" + }, + "showing_codes_for": { + "message": "次のサイトのコードを表示中:", + "description": "Smart-filter banner prefix, followed by the site domain" + }, + "theme_auto": { + "message": "自動", + "description": "Follow the system light/dark setting" + }, + "unlock": { + "message": "ロック解除", + "description": "Unlock button" + }, + "unpin": { + "message": "固定を解除", + "description": "Context-menu action to unpin an account" + }, + "vault_locked": { + "message": "保管庫はロックされています", + "description": "Title on the unlock screen" + }, + "backup_unavailable": { + "message": "未対応", + "description": "Shown on a cloud provider that is not implemented yet." } } diff --git a/_locales/ka/messages.json b/_locales/ka/messages.json index d8e35d9be..d942393d3 100644 --- a/_locales/ka/messages.json +++ b/_locales/ka/messages.json @@ -1,14 +1,22 @@ { "extName": { - "message": "აუთენთიფიკატორი", + "message": "OTPilot Authenticator", "description": "Extension Name." }, "extShortName": { - "message": "აუთენთიფიკატორი", + "message": "OTPilot", "description": "Extension Short Name." }, + "host": { + "message": "ვებსაიტი", + "description": "Bound website host used to restrict autofill to a matching page." + }, + "backup_requires_password": { + "message": "ჯერ დააყენეთ პაროლი ღრუბლოვანი სარეზერვო ასლის გამოსაყენებლად — თქვენი გასაღებები დაშიფრული იქნება ამ მოწყობილობის დატოვებამდე.", + "description": "Shown in the cloud backup section when no master password is set." + }, "extDesc": { - "message": "აუთენთიფიკატორი აგენერირებს ორფაქტორიან აუთენთიფიკაციის კოდებს თქვენს ბრაუზერში.", + "message": "OTPilot Authenticator აგენერირებს ორფაქტორიან (2FA) კოდებს, დაშიფრული ღრუბლოვანი სარეზერვო ასლით, ჰოსტთან შესაბამისი ავტომატური შევსებით და უსაფრთხოების რჩევებით.", "description": "Extension Description." }, "added": { @@ -23,6 +31,10 @@ "message": "არასწორი გასაღები", "description": "Secret Error." }, + "errorissuer": { + "message": "მომწოდებელი არ შეიძლება შეიცავდეს „::“", + "description": "Issuer Error." + }, "add_code": { "message": "ანგარიშის დამატება", "description": "Add account." @@ -36,17 +48,13 @@ "description": "Manual Entry." }, "migration_fail": { - "message": "იმპორტირება ვერ მოხერხდა. თუ თქვენ მონაცემები Google აუთენთიფიკატორიდან გადაგაქვთ, გთხოვთ, ხელახლა მოახდინოთ მონაცემების ექსპორტი და სცადოთ თავიდან.", + "message": "იმპორტირება ვერ მოხერხდა. თუ თქვენ მონაცემებს Google Authenticator-იდან გადააქვთ, გთხოვთ, ხელახლა მოახდინოთ მონაცემების ექსპორტი Google Authenticator-იდან და სცადოთ თავიდან.", "description": "Import migration data failed." }, "migration_partly_fail": { "message": "ანგარიშის ზოგიერთი მონაცემის იმპორტირება წარმატებით ვერ მოხერხდა.", "description": "Some migration data is broken." }, - "close": { - "message": "დახურვა", - "description": "Close." - }, "ok": { "message": "Ok", "description": "OK." @@ -99,10 +107,6 @@ "message": "მიმდინარე პაროლი", "description": "Current Passphrase." }, - "new_phrase": { - "message": "ახალი პაროლი", - "description": "New Passphrase." - }, "phrase": { "message": "პაროლი", "description": "Passphrase." @@ -120,19 +124,15 @@ "description": "Message that user is required to acknowledge before clearing all data." }, "delete_all": { - "message": "აუთენთიფიკატორის აღდგენა" + "message": "OTPilot-ის აღდგენა" }, "delete_all_warning": { - "message": "ეს მოქმედება წაშლის თქვენს ყველა მონაცემს და აღადგენს აუთენთიფიკატორის ყველა პარამეტრს. თქვენ ვერ შეძლებთ წაშლილი მონაცემების აღდგენას! აუთენთიფიკატორის აღდგენამდე, თქვენ უნდა შეინახოთ სარეზერვო ასლი." + "message": "ეს მოქმედება წაშლის თქვენს ყველა მონაცემს და აღადგენს OTPilot-ის ყველა პარამეტრს. თქვენ ვერ შეძლებთ წაშლილი მონაცემების აღდგენას! OTPilot-ის აღდგენამდე, თქვენ უნდა შეინახოთ სარეზერვო ასლი." }, "security_warning": { "message": "ეს პაროლი გამოყენებული იქნება თქვენი ანგარიშების დაშიფვრისთვის. ვერავინ დაგეხმარებათ, თუ დაგავიწყდათ პაროლი.", "description": "Passphrase Warning." }, - "update": { - "message": "განახლება", - "description": "Update." - }, "phrase_incorrect": { "message": "თქვენ ვერ შეძლებთ ახალი ანგარიშის დამატებას, სანამ ყველა ანგარიში არ იქნება გაშიფრული. გაგრძელებამდე, გთხოვთ, შეიყვანოთ სწორი პაროლი.", "description": "Passphrase Incorrect." @@ -157,10 +157,6 @@ "message": "უკუკავშირი", "description": "Feedback." }, - "translate": { - "message": "თარგმნა", - "description": "Translate." - }, "source": { "message": "წყაროს კოდი", "description": "Source Code." @@ -173,10 +169,6 @@ "message": "საათის Google-თან სინქრონიზაცია", "description": "Sync Clock" }, - "remember_phrase": { - "message": "პაროლის დამახსოვრება", - "description": "Remember Passphrase" - }, "clock_too_far_off": { "message": "ყურადღება! თქვენი ადგილობრივი დრო არასწორია. გაგრძელებამდე, გთხოვთ, გაასწოროთ საათი.", "description": "Local Time is Too Far Off" @@ -190,7 +182,7 @@ "description": "Capture Failed" }, "capture_local_file_failed": { - "message": "QR კოდის ლოკალური ფაილიდან დასკანერებას ცდილობთ? QR კოდის ლოკალური ფაილიდან დასკანერების ნაცვლად, გამოიყენეთ სარეზერვო QR სურათის იმპორტირება.", + "message": "QR კოდის ლოკალური ფაილიდან დასკანერებას ცდილობთ? ამის ნაცვლად გამოიყენეთ „სარეზერვო QR სურათის იმპორტირება“.", "description": "Import QR image backup instead of scan local image" }, "based_on_time": { @@ -234,13 +226,45 @@ "description": "Import qr images. Shown as add account method." }, "import_backup_code": { - "message": "ტექსტური სარეზერვო ასლის იმპორტირება", + "message": "OTPAuth URI-ის იმპორტირება", "description": "Import backup code." }, "import_otp_urls": { "message": "OTP URL მისამართების იმპორტირება", "description": "Import OTP URLs. Shown as add account method." }, + "import_drop_file": { + "message": "გადმოათრიეთ სარეზერვო ასლის ფაილი აქ ან დააჭირეთ დასათვალიერებლად", + "description": "File import dropzone title" + }, + "import_file_accepts": { + "message": "მხარდაჭერილია .json და .txt ექსპორტის ფაილები", + "description": "File import dropzone hint" + }, + "import_choose_file": { + "message": "ფაილის არჩევა", + "description": "File import dropzone button" + }, + "import_encrypted_note": { + "message": "თუ ფაილი დაშიფრულია, არჩევის შემდეგ მოგეთხოვებათ მისი პაროლის შეყვანა.", + "description": "File import note about encrypted backups" + }, + "import_enc_required": { + "message": "დაშიფრულია — საჭიროა პაროლი", + "description": "Badge on a picked encrypted backup file" + }, + "import_pass_placeholder": { + "message": "შეიყვანეთ ამ ფაილის პაროლი", + "description": "Passphrase input placeholder for encrypted import" + }, + "import_pass_hint": { + "message": "ეს არის პაროლი, რომელიც დააყენეთ სარეზერვო ასლის შექმნისას.", + "description": "Hint under the import passphrase field" + }, + "import_decrypt": { + "message": "გაშიფვრა და იმპორტირება", + "description": "Button to decrypt and import an encrypted backup" + }, "import_backup_qr_partly_failed": { "message": "იმპორტირება წარმატებით დასრულდა, მაგრამ ზოგიერთი QR კოდის ამოცნობა ვერ მოხერხდა.", "description": "Import successful, but some QR image cannot be recognized." @@ -262,7 +286,7 @@ "description": "Error password warning when import backups." }, "local_passphrase_warning": { - "message": "თქვენი პაროლი შენახულია ლოკალურად. გთხოვთ, დაუყოვნებლივ შეცვალოთ პაროლის ადგილმდებარეობა უსაფრთხოების მენიუში.", + "message": "თქვენი პაროლი ინახება ლოკალურად, გთხოვთ, დაუყოვნებლივ შეცვალოთ იგი უსაფრთხოების მენიუში.", "description": "localStorage password warning." }, "remove": { @@ -290,21 +314,113 @@ "description": "Edit" }, "manual_dropbox": { - "message": "მექანიკური სარეზერვო ასლი", + "message": "ხელით სარეზერვო ასლი", "description": "Manual backup" }, "use_autofill": { "message": "ავტომატური შევსების გამოყენება", "description": "Use Autofill" }, - "use_high_contrast": { - "message": "მაღალი კონტრასტის გამოყენება", - "description": "Use High Contrast" - }, "theme": { "message": "თემა", "description": "Theme" }, + "settings_appearance": { + "message": "გარეგნობა", + "description": "Settings section: appearance" + }, + "settings_general": { + "message": "ზოგადი", + "description": "Settings section: general" + }, + "edit_accounts": { + "message": "ანგარიშების რედაქტირება", + "description": "Header title while in edit mode" + }, + "show_qr": { + "message": "QR კოდის ჩვენება", + "description": "Context-menu action to show an account's QR code" + }, + "pin_to_top": { + "message": "თავში დამაგრება", + "description": "Context-menu action to pin an account to the top" + }, + "unpin": { + "message": "დამაგრების მოხსნა", + "description": "Context-menu action to unpin an account" + }, + "vault_locked": { + "message": "საცავი დაბლოკილია", + "description": "Title on the unlock screen" + }, + "unlock": { + "message": "განბლოკვა", + "description": "Unlock button" + }, + "qr_transfer_title": { + "message": "ამ ანგარიშის გადატანა", + "description": "QR sheet caption title" + }, + "qr_transfer_desc": { + "message": "დაასკანერეთ ეს კოდი OTPilot-ით სხვა მოწყობილობაზე, რათა გადაიტანოთ ეს ანგარიში — მონაცემები არასდროს ტოვებს თქვენს მოწყობილობებს.", + "description": "QR sheet caption body" + }, + "backup_on_device": { + "message": "ამ მოწყობილობაზე", + "description": "Backup section: local" + }, + "backup_cloud_sync": { + "message": "ღრუბლოვანი სინქრონიზაცია", + "description": "Backup section: cloud" + }, + "backup_connected": { + "message": "დაკავშირებულია", + "description": "Cloud provider connected status" + }, + "backup_not_connected": { + "message": "დაუკავშირებელია", + "description": "Cloud provider not-connected status" + }, + "showing_codes_for": { + "message": "კოდები ნაჩვენებია საიტისთვის:", + "description": "Smart-filter banner prefix, followed by the site domain" + }, + "other_accounts": { + "message": "სხვა ანგარიშები", + "description": "Divider above non-matching accounts in the filter view" + }, + "filter_to_site": { + "message": "გაფილტვრა:", + "description": "Banner prefix to re-apply the site filter, followed by the domain" + }, + "onboarding_title": { + "message": "კოდები, რომლებიც თან დაგყვებათ", + "description": "Onboarding headline" + }, + "onboarding_subtitle": { + "message": "შექმენით უსაფრთხო ორფაქტორიანი კოდები ყველა ანგარიშისთვის — დაშიფრული, ოფლაინ და ყოველთვის ერთი შეხებით დასაკოპირებელი.", + "description": "Onboarding subtitle" + }, + "onboarding_feature_offline": { + "message": "მუშაობს სრულად ოფლაინ რეჟიმში — არაფერი ტოვებს თქვენს მოწყობილობას", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_encrypted": { + "message": "დაშიფრული საცავი, სურვილისამებრ პაროლის დაბლოკვით", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_scan": { + "message": "დაასკანერეთ QR კოდი ან შემოიტანეთ არსებული სარეზერვო ასლი", + "description": "Onboarding feature bullet" + }, + "onboarding_get_started": { + "message": "დაწყება", + "description": "Onboarding primary button" + }, + "onboarding_import": { + "message": "მაქვს სარეზერვო ასლი შემოსატანად", + "description": "Onboarding secondary button" + }, "theme_light": { "message": "ღია", "description": "Light theme" @@ -313,9 +429,9 @@ "message": "მუქი", "description": "Dark theme" }, - "theme_simple": { - "message": "მარტივი", - "description": "Simple theme" + "theme_auto": { + "message": "ავტომატური", + "description": "Follow the system light/dark setting" }, "theme_compact": { "message": "კომპაქტური", @@ -325,14 +441,6 @@ "message": "მაღალი კონტრასტი", "description": "High Contrast theme" }, - "theme_flat": { - "message": "სწორი", - "description": "Flat theme" - }, - "storage_sync_info": { - "message": "ავტომატურად შექმენით თქვენი მონაცემების სარეზერვო ასლი მესამე მხარის შენახვის სერვისებში.", - "description": "3rd party backup info" - }, "browser_sync": { "message": "ბრაუზერის სინქრონიზაცია", "description": "Storage location" @@ -362,10 +470,6 @@ } } }, - "otp_unsupported_warn": { - "message": "თქვენ გაქვთ ერთი ან მეტი Steam ან Blizzard ანგარიში. დაუშიფრავი სარეზერვო ასლების შემთხვევაში არ მოხდება სტანდარტიზირებული სარეზერვო ასლის ფორმატის გამოყენება.", - "description": "Warning if using account that is not supported by standard backup format." - }, "otp_backup_inform": { "message": "თქვენ შეგიძლიათ სარეზერვო ასლების იმპორტირება სხვა აპლიკაციიდან.", "description": "Info text on import page" @@ -407,9 +511,6 @@ "backup": { "message": "სარეზერვო ასლი" }, - "backup_file_info": { - "message": "შექმენით თქვენი მონაცემების შემცველი სარეზერვო ასლის ფაილი." - }, "password_policy_default_hint": { "message": "თქვენი პაროლი არ აკმაყოფილებს თქვენი ორგანიზაციის უსაფრთხოების მოთხოვნებს. დაუკავშირდით თქვენს ადმინისტრატორს დამატებითი ინფორმაციისთვის." }, @@ -480,16 +581,16 @@ "message": "საშუალებას გაძლევთ შეხვიდეთ მესამე მხარის შენახვის სერვისებში." }, "permission_alarms": { - "message": "Allows auto-lock to work." + "message": "საშუალებას გაძლევთ, ავტომატურმა დაბლოკვამ იმუშაოს." }, "permission_scripting": { - "message": "Inject scripts into he current tab to scan QR codes and allow auto-fill to work." + "message": "შეაქვს სკრიპტები მიმდინარე ჩანართში QR კოდების დასკანერებისა და ავტომატური შევსების მუშაობისთვის." }, "permission_clipboard_write": { "message": "ანიჭებს მხოლოდ ჩაწერის წვდომას გაცვლის ბუფერზე, რათა დააკოპიროთ კოდები, ანგარიშზე დაჭერის დროს." }, "permission_context_menus": { - "message": "ამატებს აუთენთიფიკატორს კონტექსტურ მენიუში." + "message": "ამატებს OTPilot-ს კონტექსტურ მენიუში." }, "permission_sync_clock": { "message": "საშუალებას გაძლევთ, მოახდინოთ დროის სინქრონიზაცია Google-თან." @@ -500,25 +601,17 @@ "permission_dropbox_cannot_revoke": { "message": "თავდაპირველად, უნდა გამორთოთ Dropbox-ში სარეზერვო ასლის შექმნა." }, - "permission_drive": { - "message": "საშუალებას გაძლევთ, შექმნათ სარეზერვო ასლი Google Drive-ში." - }, - "permission_drive_cannot_revoke": { - "message": "თავდაპირველად, უნდა გამორთოთ Google Drive-ში სარეზერვო ასლის შექმნა." - }, - "permission_onedrive": { - "message": "საშუალებას გაძლევთ, შექმნათ სარეზერვო ასლი OneDrive-ში." - }, - "permission_onedrive_cannot_revoke": { - "message": "თავდაპირველად, უნდა გამორთოთ OneDrive-ში სარეზერვო ასლის შექმნა." - }, "permission_unknown_permission": { "message": "უცნობი ნებართვა. თუ ამ შეტყობინებას ხედავთ, გთხოვთ, შეგვატყობინოთ პრობლემის შესახებ." }, "phrase_wrong": { - "message": "Password incorrect" + "message": "პაროლი არასწორია" }, "activate_auto_filter": { - "message": "Warning: Smart filter loosely matches the domain name to an account. Always verify that you are on the correct website before entering a code!" + "message": "გაფრთხილება: ჭკვიანი ფილტრი დაახლოებით ადარებს დომენის სახელს ანგარიშს. კოდის შეყვანამდე ყოველთვის დარწმუნდით, რომ სწორ ვებსაიტზე იმყოფებით!" + }, + "backup_unavailable": { + "message": "ჯერ არ არის ხელმისაწვდომი", + "description": "Shown on a cloud provider that is not implemented yet." } } diff --git a/_locales/kaa/messages.json b/_locales/kaa/messages.json index 31a2e3666..d8742779d 100644 --- a/_locales/kaa/messages.json +++ b/_locales/kaa/messages.json @@ -1,14 +1,22 @@ { "extName": { - "message": "Autentifikator", + "message": "OTPilot Authenticator", "description": "Extension Name." }, "extShortName": { - "message": "Autentifikator", + "message": "OTPilot", "description": "Extension Short Name." }, + "host": { + "message": "Sayt", + "description": "Bound website host used to restrict autofill to a matching page." + }, + "backup_requires_password": { + "message": "Bult arqalı qosımsha qor nusqasınan paydalanıw ushın aldın parol ornatıń, sonda sırlarıńız bul qurılmadan shıǵıp ketpesten aldın shifrlanadı.", + "description": "Shown in the cloud backup section when no master password is set." + }, "extDesc": { - "message": "Autentifikator brauzerińizde eki faktorlı autentifikaciya kodların payda etedi.", + "message": "OTPilot Authenticator eki faktorlı (2FA) kodlar jaratadı, shifrlanǵan bult arqalı qosımsha qor nusqası, saytqa sáykes avtomatik toltırıw hám qáwipsizlik boyınsha keńesler beredi.", "description": "Extension Description." }, "added": { @@ -20,9 +28,13 @@ "description": "QR Error." }, "errorsecret": { - "message": "Esap sırı belgisiz", + "message": "Esap sırı jaramsız", "description": "Secret Error." }, + "errorissuer": { + "message": "Emitent \"::\" belgisin qamtıy almaydı.", + "description": "Issuer Error." + }, "add_code": { "message": "Akkaunt qosıw", "description": "Add account." @@ -36,17 +48,13 @@ "description": "Manual Entry." }, "migration_fail": { - "message": "Import ámelge aspadı. Egerde siz Google Autentifikatordan maģlıwmatlardı kóshirip atırģan bolsańız, maģlıwmatlarıńızdı Google Autentifikatordan qaytadan eksport qılıń hám qaytadan háreket etip kóriń.", + "message": "Import ámelge aspadı. Egerde siz Google Authenticator-dan maģlıwmatlardı kóshirip atırģan bolsańız, maģlıwmatlarıńızdı Google Authenticator-dan qaytadan eksport qılıń hám qaytadan háreket etip kóriń.", "description": "Import migration data failed." }, "migration_partly_fail": { "message": "Ayırım esap maģliwmatları tabıslı import qılınbadı.", "description": "Some migration data is broken." }, - "close": { - "message": "Jabıw", - "description": "Close." - }, "ok": { "message": "Maqul", "description": "OK." @@ -99,10 +107,6 @@ "message": "Házirgi Parol", "description": "Current Passphrase." }, - "new_phrase": { - "message": "Jańa parol", - "description": "New Passphrase." - }, "phrase": { "message": "Parol", "description": "Passphrase." @@ -120,29 +124,25 @@ "description": "Message that user is required to acknowledge before clearing all data." }, "delete_all": { - "message": "Autentifikatordı dáslepki jaģdayına qaytarıw" + "message": "OTPilot-tı dáslepki jaģdayına qaytarıw" }, "delete_all_warning": { - "message": "Bul bárshe maģlıwmatlarıńızdı óshirip taslaydı hámde Autentifikator tolıqlay qayta tiklenedi. Siz óshirilgen maǵlıwmatlardı qaytadan tikley almaysız! Autentifikator-dı qayta tiklewden aldın qosımsha qor nusqasın saqlaw tuwralı oylawıńız kerek." + "message": "Bul bárshe maģlıwmatlarıńızdı óshirip taslaydı hámde OTPilot-tı tolıqlay dáslepki jaģdayına qaytaradı. Siz óshirilgen maǵlıwmatlardı qaytadan tikley almaysız! OTPilot-tı dáslepki jaģdayına qaytarıwdan aldın qosımsha qor nusqasın saqlaw tuwralı oylawıńız kerek." }, "security_warning": { "message": "Usı parol esaplarıńızdı shifrlaw ushın qollanıladı. Eger paroldi umıtıp qalsańız hesh kim sizge járdem bere almaydı.", "description": "Passphrase Warning." }, - "update": { - "message": "Jańalaw", - "description": "Update." - }, "phrase_incorrect": { "message": "Bárshe esaplar paroli sheshilmegenshe siz jańa esap qosa almaysız. Dawam etiwden aldın durıs paroldi kirgiziń.", "description": "Passphrase Incorrect." }, "phrase_incorrect_export": { - "message": "Shifrdı ashıw imkáni bolmaģan esaplar, bul qosımshaģa kirgizilmiydi.", + "message": "Shifrdı ashıw imkáni bolmaģan esaplar, bul qosımsha qor nusqasına kirgizilmeydi.", "description": "Skip Unable-decripted Data." }, "phrase_not_match": { - "message": "Jasırın sóz mas kelmedi.", + "message": "Parol sáykes kelmedi.", "description": "Passphrase Not Match." }, "encrypted": { @@ -157,26 +157,18 @@ "message": "Pikir-almasiw", "description": "Feedback." }, - "translate": { - "message": "Awdarmalaw", - "description": "Translate." - }, "source": { "message": "Derek kodı", "description": "Source Code." }, "passphrase_info": { - "message": "Esap maģlıwmatlardı shifrlaw ushın paroldi kirgiziń.", + "message": "Esap maģlıwmatlarınıń shifrin ashıw ushın paroldi kirgiziń.", "description": "Passphrase Info" }, "sync_clock": { "message": "Saattı Google menen sinxronlaw", "description": "Sync Clock" }, - "remember_phrase": { - "message": "Paroldi eslep qalıw", - "description": "Remember Passphrase" - }, "clock_too_far_off": { "message": "Dıqqat! Orındaģı saatıńız júdá uzaqta, dawam etiwden aldın onı tuwırlań.", "description": "Local Time is Too Far Off" @@ -186,11 +178,11 @@ "description": "Remind Backup" }, "capture_failed": { - "message": "Súwretke alıw múmkin bolmadı, betti qaytadan júkleń yáki jáne háreket etip kóriń.", + "message": "Súwretke alıw múmkin bolmadı, betti qaytadan júkleń hám qayta háreket etip kóriń.", "description": "Capture Failed" }, "capture_local_file_failed": { - "message": "QR-kodtı fayldan skaner qılmaqshısızba? Bunıń ornına Import QR Image Backup-tan paydalanıń.", + "message": "QR-kodtı fayldan skaner qılmaqshı bolıp atırsızba? Bunıń ornına «QR súwret qosımsha qor nusqasın import qılıw»dan paydalanıń.", "description": "Import QR image backup instead of scan local image" }, "based_on_time": { @@ -202,7 +194,7 @@ "description": "Counter Based" }, "resize_popup_page": { - "message": "Qosımsha", + "message": "Qálewler", "description": "Popup Page Settings" }, "scale": { @@ -210,7 +202,7 @@ "description": "Scale" }, "export_info": { - "message": "Eskeriw: barlıq qosımsha qorlar shifrlanbaģan. Esaptı basqa baģdarlamaģa qosıwdı qáliysizbe? Qálegen esaptıń joqarı oń bólegine kursordı alıp barıń hám jasırın túymeni basıń.", + "message": "Eskeriw: barlıq qosımsha qorlar shifrlanbaģan. Esaptı basqa baģdarlamaģa qosıwdı qálaysız ba? Qálegen esaptıń joqarı oń bólegine kursordı alıp barıń hám jasırın túymeni basıń.", "description": "Export menu info text" }, "download_backup": { @@ -230,23 +222,55 @@ "description": "Import qr image backup." }, "import_qr_images": { - "message": "Import QR Images", + "message": "QR súwretlerin importlaw", "description": "Import qr images. Shown as add account method." }, "import_backup_code": { - "message": "Tekst qosımshasın import qılıw", + "message": "OTPAuth URI importlaw", "description": "Import backup code." }, "import_otp_urls": { - "message": "Import OTP URLs", + "message": "OTPAuth URI importlaw", "description": "Import OTP URLs. Shown as add account method." }, + "import_drop_file": { + "message": "Qosımsha qor faylın osı jerge taslań yamasa tańlaw ushın basıń", + "description": "File import dropzone title" + }, + "import_file_accepts": { + "message": ".json hám .txt eksportların qabıllaydı", + "description": "File import dropzone hint" + }, + "import_choose_file": { + "message": "Fayldı tańlaw", + "description": "File import dropzone button" + }, + "import_encrypted_note": { + "message": "Eger fayl shifrlanǵan bolsa, onı tańlaǵannan keyin onıń parolı soraladı.", + "description": "File import note about encrypted backups" + }, + "import_enc_required": { + "message": "Shifrlanǵan — parol talap etiledi", + "description": "Badge on a picked encrypted backup file" + }, + "import_pass_placeholder": { + "message": "Fayldıń parolın kirgiziń", + "description": "Passphrase input placeholder for encrypted import" + }, + "import_pass_hint": { + "message": "Bul — qosımsha qor nusqasın jaratqanda ornatqan parolıńız.", + "description": "Hint under the import passphrase field" + }, + "import_decrypt": { + "message": "Shifrin ashıw hám import qılıw", + "description": "Button to decrypt and import an encrypted backup" + }, "import_backup_qr_partly_failed": { "message": "Import tabıslı ámelge asırıldı, biraq ayırım QR-kodlardı tanıw múmkin bolmadı.", "description": "Import successful, but some QR image cannot be recognized." }, "import_backup_qr_in_batches": { - "message": "Qosımsha qor nusqasın toplamlarda import qılıw ushın bir neshe fayllardı saylawińiz múmkin.", + "message": "Qosımsha qor nusqasın toplamlarda import qılıw ushın bir neshe fayllardı saylawıńız múmkin.", "description": "You can select multiple image files to import backup in batches." }, "show_all_entries": { @@ -254,7 +278,7 @@ "description": "Show all entries." }, "dropbox_risk": { - "message": "Eskeriw: qosımsha qor nusqaları shifrlanbaģan. Ózińizdiń qáwip-qáterińizsiz paydalanıń.", + "message": "Eskeriw: qosımsha qor nusqaları shifrlanbaģan. Óz qáwip-qáterińizben paydalanıń.", "description": "Backup risk warning." }, "import_error_password": { @@ -270,7 +294,7 @@ "description": "Remove password." }, "download_enc_backup": { - "message": "Parol menen qorģalģan qosımsha nusqasın júklep alıń", + "message": "Parol menen qorǵalǵan qosımsha qor nusqasın júklep alıń", "description": "Download Encrypted Backup" }, "search": { @@ -290,49 +314,133 @@ "description": "Edit" }, "manual_dropbox": { - "message": "Qolda qosımshalaw", + "message": "Qolda qosımsha qor nusqasın alıw", "description": "Manual backup" }, "use_autofill": { "message": "Avtomatik toltırıwdan paydalanıń", "description": "Use Autofill" }, - "use_high_contrast": { - "message": "Joqarı Kontrasttan paydalanıń", - "description": "Use High Contrast" - }, "theme": { "message": "Tema", "description": "Theme" }, + "settings_appearance": { + "message": "Kórinis", + "description": "Settings section: appearance" + }, + "settings_general": { + "message": "Jalpı", + "description": "Settings section: general" + }, + "edit_accounts": { + "message": "Esaplardı ózgertiw", + "description": "Header title while in edit mode" + }, + "show_qr": { + "message": "QR-kodtı kórsetiw", + "description": "Context-menu action to show an account's QR code" + }, + "pin_to_top": { + "message": "Joqarıǵa bekitiw", + "description": "Context-menu action to pin an account to the top" + }, + "unpin": { + "message": "Bekitiwdi biykarlaw", + "description": "Context-menu action to unpin an account" + }, + "vault_locked": { + "message": "Qoyma qulıplanǵan", + "description": "Title on the unlock screen" + }, + "unlock": { + "message": "Qulıptan shıǵarıw", + "description": "Unlock button" + }, + "qr_transfer_title": { + "message": "Bul esaptı ótkeriw", + "description": "QR sheet caption title" + }, + "qr_transfer_desc": { + "message": "Bul esaptı basqa qurılmaǵa kóshiriw ushın basqa qurılmada OTPilot penen usı kodtı skanerleń — ol hesh qashan qurılmalarıńızdan shıqpaydı.", + "description": "QR sheet caption body" + }, + "backup_on_device": { + "message": "Usı qurılmada", + "description": "Backup section: local" + }, + "backup_cloud_sync": { + "message": "Bult arqalı sinxronlaw", + "description": "Backup section: cloud" + }, + "backup_connected": { + "message": "Jalǵanǵan", + "description": "Cloud provider connected status" + }, + "backup_not_connected": { + "message": "Jalǵanbaǵan", + "description": "Cloud provider not-connected status" + }, + "showing_codes_for": { + "message": "Kodlar kórsetilip atır:", + "description": "Smart-filter banner prefix, followed by the site domain" + }, + "other_accounts": { + "message": "Basqa esaplar", + "description": "Divider above non-matching accounts in the filter view" + }, + "filter_to_site": { + "message": "Filtrlew:", + "description": "Banner prefix to re-apply the site filter, followed by the domain" + }, + "onboarding_title": { + "message": "Sizben birge júretuǵın kodlar", + "description": "Onboarding headline" + }, + "onboarding_subtitle": { + "message": "Hár bir esap ushın qáwipsiz eki faktorlı kodlar jaratıń — shifrlanǵan, oflayn, hám hámishe bir basıw menen kóshirmege tayyar.", + "description": "Onboarding subtitle" + }, + "onboarding_feature_offline": { + "message": "Tolıq oflayn islewde — hesh nárse qurılmańızdan shıqpaydı", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_encrypted": { + "message": "Qálasańız parol qulpı qosılatuǵın shifrlanǵan saqlaw ornı", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_scan": { + "message": "QR-kodtı skanerleń yamasa bar qosımsha qor nusqasın import qılıń", + "description": "Onboarding feature bullet" + }, + "onboarding_get_started": { + "message": "Baslaw", + "description": "Onboarding primary button" + }, + "onboarding_import": { + "message": "Mende import qılatuǵın qosımsha qor nusqam bar", + "description": "Onboarding secondary button" + }, "theme_light": { - "message": "Nurlı", + "message": "Jaqtı", "description": "Light theme" }, "theme_dark": { "message": "Qarańģı", "description": "Dark theme" }, - "theme_simple": { - "message": "Ápiwayı", - "description": "Simple theme" + "theme_auto": { + "message": "Avtomatik", + "description": "Follow the system light/dark setting" }, "theme_compact": { - "message": "Kompast", + "message": "Kompakt", "description": "Compact theme" }, "theme_high_contrast": { "message": "Joqarı Kontrast", "description": "High Contrast theme" }, - "theme_flat": { - "message": "Jalpaq", - "description": "Flat theme" - }, - "storage_sync_info": { - "message": "Maģlıwmatlarıńızdı avtomatik ráwishte úshinshi tárep saqlaw xizmetlerine qosımshalań.", - "description": "3rd party backup info" - }, "browser_sync": { "message": "Brauzerdi sinxronlaw", "description": "Storage location" @@ -353,7 +461,7 @@ "description": "Sign out of 3rd party storage services" }, "token_revoked": { - "message": "$SERVICE$ esabıńızģa jalģanıwda mashqala júz berdi, qaytadan kiriń.\n", + "message": "$SERVICE$ esabıńızģa jalģanıwda mashqala júz berdi, qaytadan kiriń.", "description": "Error authenticating to backup service. $SERVICE$ will be replaced with a proper noun (E.g.: 'Google Drive' or 'Dropbox')", "placeholders": { "service": { @@ -362,10 +470,6 @@ } } }, - "otp_unsupported_warn": { - "message": "Sizde bir yáki bir neshe Steam yáki Blizzard esapları bar. Shifrlanbaģan qosımsha nusqaları standartlastırılģan qosımsha formatında paydalanbaydı.", - "description": "Warning if using account that is not supported by standard backup format." - }, "otp_backup_inform": { "message": "Siz ayırım basqa baģdarlamalardan qosımsha qor nusqaların alıp kiriwińiz múmkin.", "description": "Info text on import page" @@ -405,10 +509,7 @@ "message": "Aqıllı filtr" }, "backup": { - "message": "Awısıq qor" - }, - "backup_file_info": { - "message": "Maģlıwmatlarıńızdı faylģa qosıp qoyıń." + "message": "Qosımsha qor" }, "password_policy_default_hint": { "message": "Parolińiz shólkemińizdiń qáwipsizlik talaplarına juwap bermiydi. Qosımsha maǵlıwmat alıw ushın adminstratorıńız benen baylanısıń." @@ -432,7 +533,7 @@ "message": "Aqıllı filtrdi qosıw esaplarģa tez kiriw imkániyatın beredi." }, "show_all_insights": { - "message": "Barlıq túsnidirmelerdi kórsetiw." + "message": "Barlıq túsiniklerdi kórsetiw." }, "no_insight_available": { "message": "Hesh qanday túsinik tabılmadı, hámmesi jaqsi!" @@ -480,16 +581,16 @@ "message": "Úshinshi tárep saqlaw xizmetlerine kiriwge ruxsat beredi." }, "permission_alarms": { - "message": "Allows auto-lock to work." + "message": "Avtomatik qulıplawdıń islewine imkániyat beredi." }, "permission_scripting": { - "message": "Inject scripts into he current tab to scan QR codes and allow auto-fill to work." + "message": "QR-kodlardı skanerlew hám avtomatik toltırıwdıń islewi ushın usı betke skriptler engiziw." }, "permission_clipboard_write": { - "message": "Esap betin basqanıńızda kodlardı almastırıw buferine nusqalaw ushın tek ģana jazıw ushın buferge ruxsat beredi." + "message": "Esaptı basqanıńızda kodlardı almastırıw buferine kóshiriw ushın buferge tek jazıw ruxsatın beredi." }, "permission_context_menus": { - "message": "Konteks menyusına Autentifikator qosadı." + "message": "Konteks menyusına OTPilot qosadı." }, "permission_sync_clock": { "message": "Google menen saattı sinxronlaw imkániyatın beredi." @@ -500,25 +601,17 @@ "permission_dropbox_cannot_revoke": { "message": "Aldı menen Dropbox qosımsha qor nusqasın óshirip qoyıwıńız kerek." }, - "permission_drive": { - "message": "Google Drive-qa qosımshalaw imkániyatın beredi." - }, - "permission_drive_cannot_revoke": { - "message": "Aldı menen Google Drive qosımsha qor nusqasın óshirip qoyıwıńız kerek." - }, - "permission_onedrive": { - "message": "OneDrive-qa qosımshalaw imkánin beredi." - }, - "permission_onedrive_cannot_revoke": { - "message": "Aldı menen OneDrive qosımsha qor nusqasın óshirip qoyıwıńız kerek." - }, "permission_unknown_permission": { "message": "Belgisiz ruxsat. Eger usı xabardı kórseńiz, qátelik haqqında esabat jiberiń." }, "phrase_wrong": { - "message": "Password incorrect" + "message": "Parol qáte" }, "activate_auto_filter": { - "message": "Warning: Smart filter loosely matches the domain name to an account. Always verify that you are on the correct website before entering a code!" + "message": "Eskertiw: Aqıllı filtr domen atın esapqa juwıq túrde sáykestiredi. Kod kirgiziwden aldın hámishe durıs saytta turǵanıńızdı tekseriń!" + }, + "backup_unavailable": { + "message": "Házirshe qoljetimsiz", + "description": "Shown on a cloud provider that is not implemented yet." } } diff --git a/_locales/ko/messages.json b/_locales/ko/messages.json index 1de263b0f..7b14afc4e 100644 --- a/_locales/ko/messages.json +++ b/_locales/ko/messages.json @@ -1,14 +1,22 @@ { "extName": { - "message": "인증 도구", + "message": "OTPilot Authenticator", "description": "Extension Name." }, "extShortName": { - "message": "인증 도구", + "message": "OTPilot", "description": "Extension Short Name." }, + "host": { + "message": "웹사이트", + "description": "Bound website host used to restrict autofill to a matching page." + }, + "backup_requires_password": { + "message": "클라우드 백업을 사용하려면 먼저 비밀번호를 설정하세요. 그러면 비밀 키가 이 기기를 벗어나기 전에 암호화돼요.", + "description": "Shown in the cloud backup section when no master password is set." + }, "extDesc": { - "message": "인증 도구는 브라우저에서 2단계 인증 코드를 생성합니다.", + "message": "OTPilot Authenticator는 2단계 인증(2FA) 코드를 생성하는 확장 프로그램으로, 암호화된 클라우드 백업과 호스트 일치 자동 완성, 보안 조언 기능을 제공해요.", "description": "Extension Description." }, "added": { @@ -20,9 +28,13 @@ "description": "QR Error." }, "errorsecret": { - "message": "잘못된 계정 비밀번호예요", + "message": "잘못된 계정 비밀 키예요", "description": "Secret Error." }, + "errorissuer": { + "message": "발급자에는 \"::\"를 포함할 수 없어요", + "description": "Issuer Error." + }, "add_code": { "message": "계정 추가", "description": "Add account." @@ -36,17 +48,13 @@ "description": "Manual Entry." }, "migration_fail": { - "message": "가져오지 못했어요. Google 인증 도구로부터 데이터를 이전하셨다면, Google 인증 도구에서 데이터를 다시 내보낸 후에 다시 시도하세요.", + "message": "가져오지 못했어요. Google Authenticator에서 데이터를 이전하셨다면, Google Authenticator에서 데이터를 다시 내보낸 후에 다시 시도하세요.", "description": "Import migration data failed." }, "migration_partly_fail": { "message": "일부 계정 데이터를 가져오지 못했어요.", "description": "Some migration data is broken." }, - "close": { - "message": "닫기", - "description": "Close." - }, "ok": { "message": "확인", "description": "OK." @@ -72,7 +80,7 @@ "description": "Issuer." }, "secret": { - "message": "비밀번호", + "message": "비밀 키", "description": "Secret." }, "updateSuccess": { @@ -99,10 +107,6 @@ "message": "현재 비밀번호", "description": "Current Passphrase." }, - "new_phrase": { - "message": "새 비밀번호", - "description": "New Passphrase." - }, "phrase": { "message": "비밀번호", "description": "Passphrase." @@ -120,19 +124,15 @@ "description": "Message that user is required to acknowledge before clearing all data." }, "delete_all": { - "message": "인증 도구 초기화" + "message": "OTPilot 초기화" }, "delete_all_warning": { - "message": "이 작업은 모든 데이터를 삭제하고 인증 도구를 완전히 초기화시킬 거예요. 삭제된 어떠한 데이터도 복구할 수 없게 돼요! 인증 도구 초기화 전 백업을 저장하는 걸 고려해 보세요." + "message": "이 작업은 모든 데이터를 삭제하고 OTPilot을 완전히 초기화시킬 거예요. 삭제된 어떠한 데이터도 복구할 수 없게 돼요! OTPilot 초기화 전 백업을 저장하는 걸 고려해 보세요." }, "security_warning": { "message": "이 비밀번호는 계정을 암호화시키는 데 사용돼요. 비밀번호를 잊어버리면 아무도 도움을 줄 수 없어요.", "description": "Passphrase Warning." }, - "update": { - "message": "갱신", - "description": "Update." - }, "phrase_incorrect": { "message": "모든 계정이 복호화될 때까지 새 계정을 추가할 수 없어요. 계속하기 전 올바른 비밀번호를 입력하세요.", "description": "Passphrase Incorrect." @@ -157,10 +157,6 @@ "message": "의견 보내기", "description": "Feedback." }, - "translate": { - "message": "번역", - "description": "Translate." - }, "source": { "message": "소스 코드", "description": "Source Code." @@ -173,10 +169,6 @@ "message": "Google과 시간 동기화", "description": "Sync Clock" }, - "remember_phrase": { - "message": "비밀번호 기억", - "description": "Remember Passphrase" - }, "clock_too_far_off": { "message": "주의! 기기 시각과의 오차가 너무 커요. 계속하기 전 오차를 고쳐주세요.", "description": "Local Time is Too Far Off" @@ -234,13 +226,45 @@ "description": "Import qr images. Shown as add account method." }, "import_backup_code": { - "message": "문자 백업 가져오기", + "message": "OTPAuth URI 가져오기", "description": "Import backup code." }, "import_otp_urls": { - "message": "OTP URL 가져오기", + "message": "OTPAuth URI 가져오기", "description": "Import OTP URLs. Shown as add account method." }, + "import_drop_file": { + "message": "백업 파일을 여기로 끌어오거나 클릭해서 찾아보세요", + "description": "File import dropzone title" + }, + "import_file_accepts": { + "message": ".json 및 .txt 내보내기 파일을 지원해요", + "description": "File import dropzone hint" + }, + "import_choose_file": { + "message": "파일 선택", + "description": "File import dropzone button" + }, + "import_encrypted_note": { + "message": "파일이 암호화되어 있다면, 선택한 후 비밀번호를 입력하라는 메시지가 표시돼요.", + "description": "File import note about encrypted backups" + }, + "import_enc_required": { + "message": "암호화됨 — 비밀번호 필요", + "description": "Badge on a picked encrypted backup file" + }, + "import_pass_placeholder": { + "message": "파일의 비밀번호를 입력하세요", + "description": "Passphrase input placeholder for encrypted import" + }, + "import_pass_hint": { + "message": "백업을 만들 때 설정한 비밀번호예요.", + "description": "Hint under the import passphrase field" + }, + "import_decrypt": { + "message": "복호화 후 가져오기", + "description": "Button to decrypt and import an encrypted backup" + }, "import_backup_qr_partly_failed": { "message": "가져왔지만, 일부 QR 코드를 인식하지 못했어요.", "description": "Import successful, but some QR image cannot be recognized." @@ -297,14 +321,106 @@ "message": "자동 완성 사용", "description": "Use Autofill" }, - "use_high_contrast": { - "message": "고대비 사용", - "description": "Use High Contrast" - }, "theme": { "message": "테마", "description": "Theme" }, + "settings_appearance": { + "message": "화면", + "description": "Settings section: appearance" + }, + "settings_general": { + "message": "일반", + "description": "Settings section: general" + }, + "edit_accounts": { + "message": "계정 편집", + "description": "Header title while in edit mode" + }, + "show_qr": { + "message": "QR 코드 표시", + "description": "Context-menu action to show an account's QR code" + }, + "pin_to_top": { + "message": "맨 위에 고정", + "description": "Context-menu action to pin an account to the top" + }, + "unpin": { + "message": "고정 해제", + "description": "Context-menu action to unpin an account" + }, + "vault_locked": { + "message": "잠긴 상태", + "description": "Title on the unlock screen" + }, + "unlock": { + "message": "잠금 해제", + "description": "Unlock button" + }, + "qr_transfer_title": { + "message": "이 계정 전송", + "description": "QR sheet caption title" + }, + "qr_transfer_desc": { + "message": "다른 기기의 OTPilot으로 이 코드를 스캔하면 이 계정을 옮길 수 있어요 — 데이터는 내 기기를 벗어나지 않아요.", + "description": "QR sheet caption body" + }, + "backup_on_device": { + "message": "이 기기", + "description": "Backup section: local" + }, + "backup_cloud_sync": { + "message": "클라우드 동기화", + "description": "Backup section: cloud" + }, + "backup_connected": { + "message": "연결됨", + "description": "Cloud provider connected status" + }, + "backup_not_connected": { + "message": "연결되지 않음", + "description": "Cloud provider not-connected status" + }, + "showing_codes_for": { + "message": "다음에 대한 코드 표시:", + "description": "Smart-filter banner prefix, followed by the site domain" + }, + "other_accounts": { + "message": "기타 계정", + "description": "Divider above non-matching accounts in the filter view" + }, + "filter_to_site": { + "message": "다음으로 필터링:", + "description": "Banner prefix to re-apply the site filter, followed by the domain" + }, + "onboarding_title": { + "message": "언제나 함께하는 인증 코드", + "description": "Onboarding headline" + }, + "onboarding_subtitle": { + "message": "모든 계정에 대해 안전한 2단계 인증 코드를 생성하세요 — 암호화되고, 오프라인으로 동작하며, 언제나 탭 한 번으로 복사할 수 있어요.", + "description": "Onboarding subtitle" + }, + "onboarding_feature_offline": { + "message": "완전한 오프라인 동작 — 아무것도 기기를 벗어나지 않아요", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_encrypted": { + "message": "선택적으로 비밀번호 잠금을 설정할 수 있는 암호화된 저장소", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_scan": { + "message": "QR 코드를 스캔하거나 기존 백업을 가져오세요", + "description": "Onboarding feature bullet" + }, + "onboarding_get_started": { + "message": "시작하기", + "description": "Onboarding primary button" + }, + "onboarding_import": { + "message": "가져올 백업이 있어요", + "description": "Onboarding secondary button" + }, "theme_light": { "message": "밝게", "description": "Light theme" @@ -313,9 +429,9 @@ "message": "어둡게", "description": "Dark theme" }, - "theme_simple": { - "message": "심플", - "description": "Simple theme" + "theme_auto": { + "message": "자동", + "description": "Follow the system light/dark setting" }, "theme_compact": { "message": "콤팩트", @@ -325,14 +441,6 @@ "message": "고대비", "description": "High Contrast theme" }, - "theme_flat": { - "message": "플랫", - "description": "Flat theme" - }, - "storage_sync_info": { - "message": "제삼자 저장 서비스에 내 데이터를 자동으로 백업하세요.", - "description": "3rd party backup info" - }, "browser_sync": { "message": "브라우저 동기화", "description": "Storage location" @@ -362,10 +470,6 @@ } } }, - "otp_unsupported_warn": { - "message": "한 개 이상의 Steam 또는 Blizzard 계정을 소유하고 있어요. 암호화되지 않은 백업은 표준 백업 형식을 사용하지 않을 거예요.", - "description": "Warning if using account that is not supported by standard backup format." - }, "otp_backup_inform": { "message": "다른 애플리케이션에서 백업을 가져올 수도 있어요.", "description": "Info text on import page" @@ -387,7 +491,7 @@ "message": "고급 설정" }, "period": { - "message": "기간" + "message": "주기" }, "type": { "message": "유형" @@ -407,14 +511,11 @@ "backup": { "message": "백업" }, - "backup_file_info": { - "message": "내 데이터를 파일로 백업하세요." - }, "password_policy_default_hint": { "message": "비밀번호가 조직의 보안 요구 사항을 충족하지 않아요. 자세한 정보는 관리자에게 문의해 주세요." }, "advisor": { - "message": "권장 작업" + "message": "보안 도우미" }, "advisor_insight_password_not_set": { "message": "내 데이터를 보호하려면 비밀번호를 설정하세요." @@ -426,7 +527,7 @@ "message": "브라우저 동기화를 사용하고 있지 않아요. 사용하면 브라우저 간 계정을 동기화할 수 있게 돼요." }, "advisor_insight_auto_fill_not_enabled": { - "message": "웹 사이트에 코드를 자동으로 채울 수 있도록 자동 완성 기능을 사용할 수 있어요." + "message": "웹사이트에 코드를 자동으로 채울 수 있도록 자동 완성 기능을 사용할 수 있어요." }, "advisor_insight_smart_filter_not_enabled": { "message": "스마트 필터를 사용하면 계정에 빠르게 접근할 수 있어요." @@ -444,10 +545,10 @@ "message": "경고" }, "info": { - "message": "알림" + "message": "정보" }, "dismiss": { - "message": "지우기" + "message": "닫기" }, "learn_more": { "message": "자세히 알아보기" @@ -477,7 +578,7 @@ "message": "계정 데이터를 저장하기 위해 브라우저 저장소에 접근할 수 있게 해요." }, "permission_identity": { - "message": "제삼자 저장 서비스에 로그인할 수 있게 해요." + "message": "제삼자 저장소 서비스에 로그인할 수 있게 해요." }, "permission_alarms": { "message": "자동 잠금 동작을 허용해요." @@ -486,10 +587,10 @@ "message": "현재 탭에 스크립트를 삽입해서 QR 코드 스캔과 자동 완성 동작을 허용해요." }, "permission_clipboard_write": { - "message": "계정을 클릭할 때 클립보드에 코드를 복사해서 클립보드에 쓰기 전용 접근 권한을 줄 수 있도록 해요." + "message": "계정을 클릭했을 때 코드를 클립보드에 복사할 수 있도록 클립보드에 대한 쓰기 전용 접근 권한을 부여해요." }, "permission_context_menus": { - "message": "컨텍스트 메뉴에 인증 도구를 추가할 수 있게 해요." + "message": "컨텍스트 메뉴에 OTPilot을 추가할 수 있게 해요." }, "permission_sync_clock": { "message": "Google과 시간 동기화를 허용해요." @@ -498,19 +599,7 @@ "message": "Dropbox 백업을 허용해요." }, "permission_dropbox_cannot_revoke": { - "message": "먼저 Dropbox 백업을 사용하지 않아야 해요." - }, - "permission_drive": { - "message": "Google Drive 백업을 허용해요." - }, - "permission_drive_cannot_revoke": { - "message": "먼저 Google Drive 백업을 사용하지 않아야 해요." - }, - "permission_onedrive": { - "message": "OneDrive 백업을 허용해요." - }, - "permission_onedrive_cannot_revoke": { - "message": "먼저 OneDrive 백업을 사용하지 않아야 해요." + "message": "먼저 Dropbox 백업을 비활성화해야 해요." }, "permission_unknown_permission": { "message": "알 수 없는 권한이에요. 이 메시지를 보신다면, 버그 신고를 진행해 주세요." @@ -519,6 +608,10 @@ "message": "잘못된 비밀번호예요" }, "activate_auto_filter": { - "message": "경고: 스마트 필터는 도메인 이름과 계정을 대략적으로만 일치시켜요. 코드를 입력하기 전에 항상 올바른 웹 사이트에 있는지 확인하세요!" + "message": "경고: 스마트 필터는 도메인 이름과 계정을 대략적으로만 일치시켜요. 코드를 입력하기 전에 항상 올바른 웹사이트에 있는지 확인하세요!" + }, + "backup_unavailable": { + "message": "아직 지원되지 않아요", + "description": "Shown on a cloud provider that is not implemented yet." } } diff --git a/_locales/lt/messages.json b/_locales/lt/messages.json index 565ef8483..f767d6de7 100644 --- a/_locales/lt/messages.json +++ b/_locales/lt/messages.json @@ -1,14 +1,22 @@ { "extName": { - "message": "Autentifikatorius", + "message": "OTPilot Authenticator", "description": "Extension Name." }, "extShortName": { - "message": "Autentifikatorius", + "message": "OTPilot", "description": "Extension Short Name." }, + "host": { + "message": "Svetainė", + "description": "Bound website host used to restrict autofill to a matching page." + }, + "backup_requires_password": { + "message": "Pirmiausia nustatykite slaptažodį, kad galėtumėte naudoti debesijos atsarginę kopiją – jūsų paslaptys bus užšifruotos, prieš joms paliekant šį įrenginį.", + "description": "Shown in the cloud backup section when no master password is set." + }, "extDesc": { - "message": "Autentifikatorius generuoja dviejų veiksnių autentifikavimo kodus naršyklėje.", + "message": "OTPilot Authenticator generuoja dviejų veiksnių (2FA) kodus, siūlo šifruotą debesijos atsarginę kopiją, su svetaine susietą automatinį pildymą ir saugumo patarimus.", "description": "Extension Description." }, "added": { @@ -23,12 +31,16 @@ "message": "Netinkama paskyros paslaptis", "description": "Secret Error." }, + "errorissuer": { + "message": "Emitentas negali turėti \"::\"", + "description": "Issuer Error." + }, "add_code": { "message": "Pridėti paskyrą", "description": "Add account." }, "add_qr": { - "message": "Nuskaitykite QR kodą", + "message": "Nuskaityti QR kodą", "description": "Scan QR Code." }, "add_secret": { @@ -43,10 +55,6 @@ "message": "Kai kurie paskyros duomenys nebuvo sėkmingai importuoti.", "description": "Some migration data is broken." }, - "close": { - "message": "Uždaryti", - "description": "Close." - }, "ok": { "message": "Ok", "description": "OK." @@ -99,10 +107,6 @@ "message": "Dabartinis slaptažodis", "description": "Current Passphrase." }, - "new_phrase": { - "message": "Naujas slaptažodis", - "description": "New Passphrase." - }, "phrase": { "message": "Slaptažodis", "description": "Passphrase." @@ -120,19 +124,15 @@ "description": "Message that user is required to acknowledge before clearing all data." }, "delete_all": { - "message": "Iš naujo nustatyti autentifikatorių" + "message": "Iš naujo nustatyti OTPilot" }, "delete_all_warning": { - "message": "Taip ištrinsite visus duomenis ir visiškai iš naujo nustatysite Autentifikatorių. Negalėsite atkurti jokių ištrintų duomenų! Prieš iš naujo nustatydami Autentifikatorių, turėtumėte išsaugoti atsarginę kopiją." + "message": "Taip ištrinsite visus duomenis ir visiškai iš naujo nustatysite OTPilot. Negalėsite atkurti jokių ištrintų duomenų! Prieš iš naujo nustatydami OTPilot, turėtumėte išsaugoti atsarginę kopiją." }, "security_warning": { "message": "Šis slaptažodis bus naudojamas jūsų paskyroms šifruoti. Jei slaptažodį pamiršite, niekas jums nepadės.", "description": "Passphrase Warning." }, - "update": { - "message": "Atnaujinti", - "description": "Update." - }, "phrase_incorrect": { "message": "Negalite pridėti naujos paskyros, kol visos paskyros neiššifruotos. Prieš tęsdami įveskite teisingą slaptažodį.", "description": "Passphrase Incorrect." @@ -146,7 +146,7 @@ "description": "Passphrase Not Match." }, "encrypted": { - "message": "Užšifruotas", + "message": "Užšifruota", "description": "Encrypted." }, "copied": { @@ -157,10 +157,6 @@ "message": "Atsiliepimai", "description": "Feedback." }, - "translate": { - "message": "Versti", - "description": "Translate." - }, "source": { "message": "Programinis kodas", "description": "Source Code." @@ -170,13 +166,9 @@ "description": "Passphrase Info" }, "sync_clock": { - "message": "Laikrodžio sinchronizavimas su \"Google", + "message": "Laikrodžio sinchronizavimas su \"Google\"", "description": "Sync Clock" }, - "remember_phrase": { - "message": "Įsiminti slaptažodį", - "description": "Remember Passphrase" - }, "clock_too_far_off": { "message": "Dėmesio! Jūsų vietinis laikrodis yra per daug nutolęs, prieš tęsdami darbą jį pataisykite.", "description": "Local Time is Too Far Off" @@ -190,7 +182,7 @@ "description": "Capture Failed" }, "capture_local_file_failed": { - "message": "Ar bandote nuskaityti QR kodą iš vietinio failo? Vietoj to naudokite \"Import QR Image Backup\".", + "message": "Ar bandote nuskaityti QR kodą iš vietinio failo? Vietoj to naudokite parinktį „Importuoti QR atvaizdo atsarginę kopiją“.", "description": "Import QR image backup instead of scan local image" }, "based_on_time": { @@ -198,11 +190,11 @@ "description": "Time Based" }, "based_on_counter": { - "message": "Skaičiuokle pagrįstas", + "message": "Pagal skaitiklį", "description": "Counter Based" }, "resize_popup_page": { - "message": "Nustatymai", + "message": "Parinktys", "description": "Popup Page Settings" }, "scale": { @@ -210,7 +202,7 @@ "description": "Scale" }, "export_info": { - "message": "Įspėjimas: visos atsarginės kopijos yra nešifruojamos. Norite pridėti paskyrą prie kitos programos? Užveskite pelės žymeklį ant bet kurios paskyros viršutinės dešiniosios dalies ir paspauskite paslėptą mygtuką.", + "message": "Įspėjimas: visos atsarginės kopijos yra neužšifruotos. Norite pridėti paskyrą prie kitos programos? Užveskite pelės žymeklį ant bet kurios paskyros viršutinės dešiniosios dalies ir paspauskite paslėptą mygtuką.", "description": "Export menu info text" }, "download_backup": { @@ -222,7 +214,7 @@ "description": "Import backup." }, "import_backup_file": { - "message": "Importuoti atsarginę kopiją", + "message": "Importuoti atsarginės kopijos failą", "description": "Import backup file." }, "import_backup_qr": { @@ -230,23 +222,55 @@ "description": "Import qr image backup." }, "import_qr_images": { - "message": "Import QR Images", + "message": "Importuoti QR atvaizdus", "description": "Import qr images. Shown as add account method." }, "import_backup_code": { - "message": "Importuoti teksto atsarginę kopiją", + "message": "Importuoti OTPAuth URI", "description": "Import backup code." }, "import_otp_urls": { - "message": "Import OTP URLs", + "message": "Importuoti OTPAuth URI", "description": "Import OTP URLs. Shown as add account method." }, + "import_drop_file": { + "message": "Nuvilkite atsarginės kopijos failą arba spustelėkite, kad naršytumėte", + "description": "File import dropzone title" + }, + "import_file_accepts": { + "message": "Priimami .json ir .txt eksportuoti failai", + "description": "File import dropzone hint" + }, + "import_choose_file": { + "message": "Pasirinkti failą", + "description": "File import dropzone button" + }, + "import_encrypted_note": { + "message": "Jei failas užšifruotas, pasirinkę jį būsite paprašyti įvesti slaptažodį.", + "description": "File import note about encrypted backups" + }, + "import_enc_required": { + "message": "Užšifruota — reikalingas slaptažodis", + "description": "Badge on a picked encrypted backup file" + }, + "import_pass_placeholder": { + "message": "Įveskite failo slaptažodį", + "description": "Passphrase input placeholder for encrypted import" + }, + "import_pass_hint": { + "message": "Tai slaptažodis, kurį nustatėte kurdami atsarginę kopiją.", + "description": "Hint under the import passphrase field" + }, + "import_decrypt": { + "message": "Iššifruoti ir importuoti", + "description": "Button to decrypt and import an encrypted backup" + }, "import_backup_qr_partly_failed": { "message": "Importas pavyko, tačiau kai kurių QR kodų nepavyko atpažinti.", "description": "Import successful, but some QR image cannot be recognized." }, "import_backup_qr_in_batches": { - "message": "Galite pasirinkti kelis failus importuoti atsarginę kopiją dalimis.", + "message": "Galite pasirinkti kelis failus ir importuoti atsarginę kopiją dalimis.", "description": "You can select multiple image files to import backup in batches." }, "show_all_entries": { @@ -254,7 +278,7 @@ "description": "Show all entries." }, "dropbox_risk": { - "message": "Demesio: atsarginės kopijos yra nešifruojamos. Naudokite savo rizika.", + "message": "Dėmesio: atsarginės kopijos yra neužšifruotos. Naudokitės savo rizika.", "description": "Backup risk warning." }, "import_error_password": { @@ -270,7 +294,7 @@ "description": "Remove password." }, "download_enc_backup": { - "message": "Atsisiųsti Slaptažodžiu apsaugotą atsarginę kopiją", + "message": "Atsisiųsti slaptažodžiu apsaugotą atsarginę kopiją", "description": "Download Encrypted Backup" }, "search": { @@ -290,49 +314,133 @@ "description": "Edit" }, "manual_dropbox": { - "message": "Rankinis atsarginis kopijavimas", + "message": "Rankinė atsarginė kopija", "description": "Manual backup" }, "use_autofill": { "message": "Naudoti automatinį pildymą", "description": "Use Autofill" }, - "use_high_contrast": { - "message": "Naudokite didelį kontrastą", - "description": "Use High Contrast" - }, "theme": { "message": "Tema", "description": "Theme" }, + "settings_appearance": { + "message": "Išvaizda", + "description": "Settings section: appearance" + }, + "settings_general": { + "message": "Bendra", + "description": "Settings section: general" + }, + "edit_accounts": { + "message": "Redaguoti paskyras", + "description": "Header title while in edit mode" + }, + "show_qr": { + "message": "Rodyti QR kodą", + "description": "Context-menu action to show an account's QR code" + }, + "pin_to_top": { + "message": "Prisegti viršuje", + "description": "Context-menu action to pin an account to the top" + }, + "unpin": { + "message": "Atsegti", + "description": "Context-menu action to unpin an account" + }, + "vault_locked": { + "message": "Saugykla užrakinta", + "description": "Title on the unlock screen" + }, + "unlock": { + "message": "Atrakinti", + "description": "Unlock button" + }, + "qr_transfer_title": { + "message": "Perkelti šią paskyrą", + "description": "QR sheet caption title" + }, + "qr_transfer_desc": { + "message": "Nuskaitykite šį kodą su OTPilot kitame įrenginyje, kad perkeltumėte šią paskyrą — ji niekada nepalieka jūsų įrenginių.", + "description": "QR sheet caption body" + }, + "backup_on_device": { + "message": "Šiame įrenginyje", + "description": "Backup section: local" + }, + "backup_cloud_sync": { + "message": "Debesijos sinchronizavimas", + "description": "Backup section: cloud" + }, + "backup_connected": { + "message": "Prisijungta", + "description": "Cloud provider connected status" + }, + "backup_not_connected": { + "message": "Neprisijungta", + "description": "Cloud provider not-connected status" + }, + "showing_codes_for": { + "message": "Rodomi kodai svetainei:", + "description": "Smart-filter banner prefix, followed by the site domain" + }, + "other_accounts": { + "message": "Kitos paskyros", + "description": "Divider above non-matching accounts in the filter view" + }, + "filter_to_site": { + "message": "Filtruoti pagal:", + "description": "Banner prefix to re-apply the site filter, followed by the domain" + }, + "onboarding_title": { + "message": "Kodai, kurie visada su jumis", + "description": "Onboarding headline" + }, + "onboarding_subtitle": { + "message": "Generuokite saugius dviejų veiksnių kodus kiekvienai paskyrai — užšifruotus, veikiančius neprisijungus ir visada pasiekiamus vienu palietimu.", + "description": "Onboarding subtitle" + }, + "onboarding_feature_offline": { + "message": "Veikia visiškai neprisijungus — niekas nepalieka jūsų įrenginio", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_encrypted": { + "message": "Užšifruota saugykla su neprivalomu slaptažodžio užraktu", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_scan": { + "message": "Nuskaitykite QR kodą arba importuokite esamą atsarginę kopiją", + "description": "Onboarding feature bullet" + }, + "onboarding_get_started": { + "message": "Pradėti", + "description": "Onboarding primary button" + }, + "onboarding_import": { + "message": "Turiu atsarginę kopiją, kurią noriu importuoti", + "description": "Onboarding secondary button" + }, "theme_light": { - "message": "Šviesa", + "message": "Šviesi", "description": "Light theme" }, "theme_dark": { - "message": "Tamsus", + "message": "Tamsi", "description": "Dark theme" }, - "theme_simple": { - "message": "Paprastas", - "description": "Simple theme" + "theme_auto": { + "message": "Automatinė", + "description": "Follow the system light/dark setting" }, "theme_compact": { - "message": "Kompaktiškas", + "message": "Kompaktiška", "description": "Compact theme" }, "theme_high_contrast": { "message": "Didelis kontrastas", "description": "High Contrast theme" }, - "theme_flat": { - "message": "Plokščias", - "description": "Flat theme" - }, - "storage_sync_info": { - "message": "Automatiškai kurkite atsargines duomenų kopijas į trečiųjų šalių saugojimo paslaugas.", - "description": "3rd party backup info" - }, "browser_sync": { "message": "Naršyklės sinchronizavimas", "description": "Storage location" @@ -353,7 +461,7 @@ "description": "Sign out of 3rd party storage services" }, "token_revoked": { - "message": "Kilo problema jungiantis prie jūsų $SERVICE$ paskyros, bandykite dar kartą.", + "message": "Kilo problema jungiantis prie jūsų $SERVICE$ paskyros, prisijunkite dar kartą.", "description": "Error authenticating to backup service. $SERVICE$ will be replaced with a proper noun (E.g.: 'Google Drive' or 'Dropbox')", "placeholders": { "service": { @@ -362,10 +470,6 @@ } } }, - "otp_unsupported_warn": { - "message": "Turite vieną ar daugiau \"Steam\" arba \"Blizzard\" paskyrų. Neužšifruotoms atsarginėms kopijoms nebus naudojamas standartinis atsarginių kopijų formatas.", - "description": "Warning if using account that is not supported by standard backup format." - }, "otp_backup_inform": { "message": "Galite importuoti atsargines kopijas iš kai kurių kitų programų.", "description": "Info text on import page" @@ -407,9 +511,6 @@ "backup": { "message": "Atsarginė kopija" }, - "backup_file_info": { - "message": "Sukurkite atsarginę duomenų kopiją į failą." - }, "password_policy_default_hint": { "message": "Jūsų slaptažodis neatitinka jūsų organizacijos saugumo reikalavimų. Dėl papildomos informacijos kreipkitės į administratorių." }, @@ -462,10 +563,10 @@ "message": "Leidimai" }, "permission_revoke": { - "message": "Atimti" + "message": "Atšaukti" }, "permission_show_required_permissions": { - "message": "Rodyti leidimus kurių negalima atšaukti" + "message": "Rodyti leidimus, kurių negalima atšaukti" }, "permission_required": { "message": "Šis leidimas yra privalomas ir negali būti atšauktas." @@ -480,16 +581,16 @@ "message": "Leidžia prisijungti prie trečiosios šalies saugojimo paslaugų." }, "permission_alarms": { - "message": "Allows auto-lock to work." + "message": "Leidžia veikti automatiniam užrakinimui." }, "permission_scripting": { - "message": "Inject scripts into he current tab to scan QR codes and allow auto-fill to work." + "message": "Įterpia scenarijus į dabartinį skirtuką, kad būtų galima nuskaityti QR kodus ir veiktų automatinis pildymas." }, "permission_clipboard_write": { "message": "Suteikiama tik rašymo prieiga prie iškarpinės, kad, spustelėjus paskyrą, kodai būtų kopijuojami į iškarpinę." }, "permission_context_menus": { - "message": "Į kontekstinį meniu įtraukiamas autentifikatorius." + "message": "Į kontekstinį meniu įtraukiamas OTPilot." }, "permission_sync_clock": { "message": "Leidžia sinchronizuoti laikrodį su \"Google\"." @@ -500,25 +601,17 @@ "permission_dropbox_cannot_revoke": { "message": "Pirmiausia turite išjungti \"Dropbox\" atsarginę kopiją." }, - "permission_drive": { - "message": "Leidžia kurti atsarginę kopiją \"Google\" diske." - }, - "permission_drive_cannot_revoke": { - "message": "Pirmiausia turite išjungti \"Google Drive\" atsarginę kopiją." - }, - "permission_onedrive": { - "message": "Leidžia kurti atsarginę kopiją \"OneDrive\"." - }, - "permission_onedrive_cannot_revoke": { - "message": "Pirmiausia turite išjungti \"OneDrive\" atsarginę kopiją." - }, "permission_unknown_permission": { "message": "Nežinomas leidimas. Jei matote šį pranešimą, atsiųskite pranešimą apie klaidą." }, "phrase_wrong": { - "message": "Password incorrect" + "message": "Neteisingas slaptažodis" }, "activate_auto_filter": { - "message": "Warning: Smart filter loosely matches the domain name to an account. Always verify that you are on the correct website before entering a code!" + "message": "Įspėjimas: išmanusis filtras laisvai susieja domeno pavadinimą su paskyra. Prieš įvesdami kodą visada patikrinkite, ar esate tinkamoje svetainėje!" + }, + "backup_unavailable": { + "message": "Kol kas nepasiekiama", + "description": "Shown on a cloud provider that is not implemented yet." } } diff --git a/_locales/lv/messages.json b/_locales/lv/messages.json index 3e17c461c..41d07eec4 100644 --- a/_locales/lv/messages.json +++ b/_locales/lv/messages.json @@ -1,14 +1,22 @@ { "extName": { - "message": "Divpakāpju kodu ģenerators", + "message": "OTPilot Authenticator", "description": "Extension Name." }, "extShortName": { - "message": "Divpakāpju kodu ģenerators", + "message": "OTPilot", "description": "Extension Short Name." }, + "host": { + "message": "Vietne", + "description": "Bound website host used to restrict autofill to a matching page." + }, + "backup_requires_password": { + "message": "Lai izmantotu mākoņa rezerves kopiju, vispirms iestatiet paroli — tādējādi jūsu slepenās atslēgas tiek šifrētas, pirms tās pamet šo ierīci.", + "description": "Shown in the cloud backup section when no master password is set." + }, "extDesc": { - "message": "Divpakāpju kodu ģenerators atvieglo darbu ar 2FA (divpakāpju) autentifikācijas kodu izveidošanu un aizpildīšanu jūsu pārlūkprogrammā", + "message": "OTPilot Authenticator ģenerē divfaktoru (2FA) autentifikācijas kodus ar šifrētu mākoņa rezerves kopēšanu, vietnei piesaistītu automātisko aizpildīšanu un drošības ieteikumiem.", "description": "Extension Description." }, "added": { @@ -16,19 +24,23 @@ "description": "Added Account." }, "errorqr": { - "message": "Neatpazīstams kvadrātkods (QR kods).", + "message": "Neatpazīstams QR kods.", "description": "QR Error." }, "errorsecret": { "message": "Konta slepenā atslēga nav derīga", "description": "Secret Error." }, + "errorissuer": { + "message": "Izsniedzējs nedrīkst saturēt \"::\"", + "description": "Issuer Error." + }, "add_code": { "message": "Pievienot kontu", "description": "Add account." }, "add_qr": { - "message": "Nolasīt kvadrātkodu (QR kodu)", + "message": "Skenēt QR kodu", "description": "Scan QR Code." }, "add_secret": { @@ -43,10 +55,6 @@ "message": "Daži konta dati netika veiksmīgi importēti.", "description": "Some migration data is broken." }, - "close": { - "message": "Aizvērt", - "description": "Close." - }, "ok": { "message": "Labi", "description": "OK." @@ -99,10 +107,6 @@ "message": "Pašreizējā parole", "description": "Current Passphrase." }, - "new_phrase": { - "message": "Jaunā parole", - "description": "New Passphrase." - }, "phrase": { "message": "Parole", "description": "Passphrase." @@ -120,19 +124,15 @@ "description": "Message that user is required to acknowledge before clearing all data." }, "delete_all": { - "message": "Atiestatīt Divpakāpju kodu ģeneratoru" + "message": "Atiestatīt OTPilot" }, "delete_all_warning": { - "message": "Tiks dzēsti visi jūsu dati un pilnībā tiks atiestatīts Divpakāpju kodu ģenerators. Jūs nevarēsiet atgūt izdzēstos datus! Pirms Divpakāpju kodu ģeneratora atiestatīšanas apsveriet iespēju veikt rezerves kopiju." + "message": "Tiks dzēsti visi jūsu dati un pilnībā tiks atiestatīts OTPilot. Jūs nevarēsiet atgūt izdzēstos datus! Pirms OTPilot atiestatīšanas apsveriet iespēju veikt rezerves kopiju." }, "security_warning": { "message": "Šī parole tiks izmantota jūsu kontu šifrēšanai. Neviens nevarēs jums palīdzēt, ja aizmirsīsiet paroli.", "description": "Passphrase Warning." }, - "update": { - "message": "Atjaunināt", - "description": "Update." - }, "phrase_incorrect": { "message": "Jūs nevarat pievienot jaunu kontu, kamēr visi konti nav atšifrēti. Lai turpināt, lūdzu, ievadiet pareizo paroli.", "description": "Passphrase Incorrect." @@ -157,10 +157,6 @@ "message": "Atsauksmes", "description": "Feedback." }, - "translate": { - "message": "Tulkot", - "description": "Translate." - }, "source": { "message": "Avota kods", "description": "Source Code." @@ -173,12 +169,8 @@ "message": "Sinhronizēt laiku ar Google", "description": "Sync Clock" }, - "remember_phrase": { - "message": "Atcerēties paroli", - "description": "Remember Passphrase" - }, "clock_too_far_off": { - "message": "Uzmanību bīstami! Jūsu vietējais laiks ir ar pārāk lielu laika nobīdi. Lūdzu, sinhronizējiet laiku ar kādu laika serveri, pirms turpināt.", + "message": "Uzmanību! Jūsu vietējais pulkstenis ir pārāk nobīdīts, lūdzu, salabojiet to, pirms turpināt.", "description": "Local Time is Too Far Off" }, "remind_backup": { @@ -190,7 +182,7 @@ "description": "Capture Failed" }, "capture_local_file_failed": { - "message": "Vai mēģināt nolasīt kvadrātkodu (QR kodu) no lokāla faila? Tā vietā izmantojiet Importēt kvadrātkoda (QR) attēla rezerves kopiju.", + "message": "Vai mēģināt skenēt QR kodu no lokāla faila? Tā vietā izmantojiet funkciju „Importēt QR koda attēla rezerves kopiju“.", "description": "Import QR image backup instead of scan local image" }, "based_on_time": { @@ -214,7 +206,7 @@ "description": "Export menu info text" }, "download_backup": { - "message": "Saglabāt rezerves kopijas failu", + "message": "Lejupielādēt rezerves kopijas failu", "description": "Download backup file." }, "import_backup": { @@ -226,27 +218,59 @@ "description": "Import backup file." }, "import_backup_qr": { - "message": "Importēt kvadrātkoda (QR) attēla rezerves kopiju", + "message": "Importēt QR koda attēla rezerves kopiju", "description": "Import qr image backup." }, "import_qr_images": { - "message": "Import QR Images", + "message": "Importēt QR attēlus", "description": "Import qr images. Shown as add account method." }, "import_backup_code": { - "message": "Importēt teksta rezerves kopiju", + "message": "Importēt OTPAuth URI", "description": "Import backup code." }, "import_otp_urls": { - "message": "Import OTP URLs", + "message": "Importēt OTPAuth URI", "description": "Import OTP URLs. Shown as add account method." }, + "import_drop_file": { + "message": "Nometiet rezerves kopijas failu šeit vai noklikšķiniet, lai pārlūkotu", + "description": "File import dropzone title" + }, + "import_file_accepts": { + "message": "Pieņem .json un .txt eksportus", + "description": "File import dropzone hint" + }, + "import_choose_file": { + "message": "Izvēlēties failu", + "description": "File import dropzone button" + }, + "import_encrypted_note": { + "message": "Ja fails ir šifrēts, pēc tā atlasīšanas jums tiks pieprasīta tā parole.", + "description": "File import note about encrypted backups" + }, + "import_enc_required": { + "message": "Šifrēts — nepieciešama parole", + "description": "Badge on a picked encrypted backup file" + }, + "import_pass_placeholder": { + "message": "Ievadiet faila paroli", + "description": "Passphrase input placeholder for encrypted import" + }, + "import_pass_hint": { + "message": "Šī ir parole, kuru iestatījāt, izveidojot rezerves kopiju.", + "description": "Hint under the import passphrase field" + }, + "import_decrypt": { + "message": "Atšifrēt un importēt", + "description": "Button to decrypt and import an encrypted backup" + }, "import_backup_qr_partly_failed": { - "message": "Importēšana bija veiksmīga, taču dažus kvadrātkodus (QR kodus) nevarēja atpazīt.", + "message": "Importēšana bija veiksmīga, taču dažus QR kodus nevarēja atpazīt.", "description": "Import successful, but some QR image cannot be recognized." }, "import_backup_qr_in_batches": { - "message": "Variet atlasīt vairākus failus, lai importētu rezerves kopiju vienu pēc otra.", + "message": "Varat atlasīt vairākus failus, lai importētu rezerves kopijas pakešveidā.", "description": "You can select multiple image files to import backup in batches." }, "show_all_entries": { @@ -270,7 +294,7 @@ "description": "Remove password." }, "download_enc_backup": { - "message": "Lejupielādēt rezerves kopiju aizsargātu ar paroli", + "message": "Lejupielādēt ar paroli aizsargātu rezerves kopiju", "description": "Download Encrypted Backup" }, "search": { @@ -290,21 +314,113 @@ "description": "Edit" }, "manual_dropbox": { - "message": "Manuāla dublēšana", + "message": "Manuāla rezerves kopija", "description": "Manual backup" }, "use_autofill": { "message": "Izmantot automātisko aizpildīšanu", "description": "Use Autofill" }, - "use_high_contrast": { - "message": "Izmantot spilgtu kontrastu", - "description": "Use High Contrast" - }, "theme": { "message": "Izskats", "description": "Theme" }, + "settings_appearance": { + "message": "Ārējais izskats", + "description": "Settings section: appearance" + }, + "settings_general": { + "message": "Vispārīgi", + "description": "Settings section: general" + }, + "edit_accounts": { + "message": "Rediģēt kontus", + "description": "Header title while in edit mode" + }, + "show_qr": { + "message": "Rādīt QR kodu", + "description": "Context-menu action to show an account's QR code" + }, + "pin_to_top": { + "message": "Piespraust augšpusē", + "description": "Context-menu action to pin an account to the top" + }, + "unpin": { + "message": "Atspraust", + "description": "Context-menu action to unpin an account" + }, + "vault_locked": { + "message": "Glabātuve aizslēgta", + "description": "Title on the unlock screen" + }, + "unlock": { + "message": "Atslēgt", + "description": "Unlock button" + }, + "qr_transfer_title": { + "message": "Pārsūtīt šo kontu", + "description": "QR sheet caption title" + }, + "qr_transfer_desc": { + "message": "Noskenējiet šo kodu ar OTPilot citā ierīcē, lai pārvietotu šo kontu — tas nekad nepamet jūsu ierīces.", + "description": "QR sheet caption body" + }, + "backup_on_device": { + "message": "Šajā ierīcē", + "description": "Backup section: local" + }, + "backup_cloud_sync": { + "message": "Mākoņa sinhronizācija", + "description": "Backup section: cloud" + }, + "backup_connected": { + "message": "Savienots", + "description": "Cloud provider connected status" + }, + "backup_not_connected": { + "message": "Nav savienots", + "description": "Cloud provider not-connected status" + }, + "showing_codes_for": { + "message": "Rāda kodus vietnei:", + "description": "Smart-filter banner prefix, followed by the site domain" + }, + "other_accounts": { + "message": "Citi konti", + "description": "Divider above non-matching accounts in the filter view" + }, + "filter_to_site": { + "message": "Filtrēt vietnei:", + "description": "Banner prefix to re-apply the site filter, followed by the domain" + }, + "onboarding_title": { + "message": "Kodi, kas ceļo līdzi jums", + "description": "Onboarding headline" + }, + "onboarding_subtitle": { + "message": "Ģenerējiet drošus divpakāpju kodus katram kontam — šifrētus, bezsaistes un vienmēr tikai vienu pieskārienu no kopēšanas.", + "description": "Onboarding subtitle" + }, + "onboarding_feature_offline": { + "message": "Darbojas pilnībā bezsaistē — nekas nepamet jūsu ierīci", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_encrypted": { + "message": "Šifrēta glabātuve ar izvēles paroles aizsardzību", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_scan": { + "message": "Noskenējiet QR kodu vai importējiet esošu rezerves kopiju", + "description": "Onboarding feature bullet" + }, + "onboarding_get_started": { + "message": "Sākt darbu", + "description": "Onboarding primary button" + }, + "onboarding_import": { + "message": "Man ir rezerves kopija, ko importēt", + "description": "Onboarding secondary button" + }, "theme_light": { "message": "Gaišais", "description": "Light theme" @@ -313,26 +429,18 @@ "message": "Tumšais", "description": "Dark theme" }, - "theme_simple": { - "message": "Vienkāršais", - "description": "Simple theme" + "theme_auto": { + "message": "Automātiski", + "description": "Follow the system light/dark setting" }, "theme_compact": { "message": "Kompaktais", "description": "Compact theme" }, "theme_high_contrast": { - "message": "Spilgta kontrasta", + "message": "Augsts kontrasts", "description": "High Contrast theme" }, - "theme_flat": { - "message": "Plakanais", - "description": "Flat theme" - }, - "storage_sync_info": { - "message": "Automātiski veikt datu rezerves kopiju trešās puses krātuves pakalpojumos.", - "description": "3rd party backup info" - }, "browser_sync": { "message": "Pārlūkprogrammas sinhronizēšana", "description": "Storage location" @@ -362,10 +470,6 @@ } } }, - "otp_unsupported_warn": { - "message": "Jums ir viens vai vairāki Steam vai Blizzard konti. Nešifrētās rezerves kopijas neizmantos standartizēto rezerves kopijas formātu.", - "description": "Warning if using account that is not supported by standard backup format." - }, "otp_backup_inform": { "message": "Varat importēt rezerves kopijas no dažām citām programmām.", "description": "Info text on import page" @@ -387,7 +491,7 @@ "message": "Papildu" }, "period": { - "message": "Laika termiņš" + "message": "Periods" }, "type": { "message": "Tips" @@ -407,9 +511,6 @@ "backup": { "message": "Rezerves kopija" }, - "backup_file_info": { - "message": "Izveidot datu rezerves kopiju failā." - }, "password_policy_default_hint": { "message": "Jūsu parole neatbilst jūsu organizācijas drošības prasībām. Lai iegūtu papildinformāciju, sazinieties ar savu administratoru." }, @@ -471,7 +572,7 @@ "message": "Šī atļauja ir nepieciešama, un to nevar atsaukt." }, "permission_active_tab": { - "message": "Piekļuve pašreizējai cilnei, lai varētu nolasīt kvadrātkodus (QR kodus)." + "message": "Piekļuve pašreizējai cilnei, lai varētu skenēt QR kodus." }, "permission_storage": { "message": "Piekļuve pārlūkprogrammas krātuvei, lai varētu saglabāt konta datus." @@ -480,16 +581,16 @@ "message": "Ļauj autorizēties trešās puses krātuves pakalpojumos." }, "permission_alarms": { - "message": "Allows auto-lock to work." + "message": "Ļauj darboties automātiskajai aizslēgšanai." }, "permission_scripting": { - "message": "Inject scripts into he current tab to scan QR codes and allow auto-fill to work." + "message": "Ievieto skriptus pašreizējā cilnē, lai varētu skenēt QR kodus un darbotos automātiskā aizpildīšana." }, "permission_clipboard_write": { "message": "Piešķir tikai rakstīšanas piekļuvi starpliktuvei, lai varētu kopēt kodus starpliktuvē, kad noklikšķināt uz konta." }, "permission_context_menus": { - "message": "Pievieno Divpakāpju kodu ģeneratoru konteksta izvēlnei." + "message": "Pievieno OTPilot konteksta izvēlnei." }, "permission_sync_clock": { "message": "Atļauj sinhronizēt laiku ar Google." @@ -500,25 +601,17 @@ "permission_dropbox_cannot_revoke": { "message": "Jums vispirms ir jāatslēdz Dropbox rezerves kopija." }, - "permission_drive": { - "message": "Atļauj veikt rezerves kopiju Google Drive." - }, - "permission_drive_cannot_revoke": { - "message": "Jums vispirms ir jāatslēdz Google Drive rezerves kopija." - }, - "permission_onedrive": { - "message": "Atļauj veikt rezerves kopiju OneDrive." - }, - "permission_onedrive_cannot_revoke": { - "message": "Jums vispirms ir jāatslēdz OneDrive rezerves kopija." - }, "permission_unknown_permission": { "message": "Nezināma atļauja. Ja redzat šo ziņojumu, lūdzu, nosūtiet kļūdu ziņojumu." }, "phrase_wrong": { - "message": "Password incorrect" + "message": "Nepareiza parole" }, "activate_auto_filter": { - "message": "Warning: Smart filter loosely matches the domain name to an account. Always verify that you are on the correct website before entering a code!" + "message": "Brīdinājums: viedais filtrs aptuveni sasaista domēna nosaukumu ar kontu. Vienmēr pārliecinieties, ka atrodaties pareizajā vietnē, pirms ievadāt kodu!" + }, + "backup_unavailable": { + "message": "Vēl nav pieejams", + "description": "Shown on a cloud provider that is not implemented yet." } } diff --git a/_locales/nl/messages.json b/_locales/nl/messages.json index e627be5bf..ca909c7b9 100644 --- a/_locales/nl/messages.json +++ b/_locales/nl/messages.json @@ -1,14 +1,22 @@ { "extName": { - "message": "Authenticator", + "message": "OTPilot Authenticator", "description": "Extension Name." }, "extShortName": { - "message": "Authenticator", + "message": "OTPilot", "description": "Extension Short Name." }, + "host": { + "message": "Website", + "description": "Bound website host used to restrict autofill to a matching page." + }, + "backup_requires_password": { + "message": "Stel eerst een wachtwoord in om cloudback-up te gebruiken, zodat uw geheimen worden versleuteld voordat ze dit apparaat verlaten.", + "description": "Shown in the cloud backup section when no master password is set." + }, "extDesc": { - "message": "Authenticator genereert 2-stapsverificatiecodes in uw browser.", + "message": "OTPilot Authenticator genereert tweefactorcodes (2FA), met versleutelde cloudback-up, host-gebonden automatisch invullen en beveiligingsadvies.", "description": "Extension Description." }, "added": { @@ -23,6 +31,10 @@ "message": "Ongeldig accountgeheim", "description": "Secret Error." }, + "errorissuer": { + "message": "Uitgever mag geen \"::\" bevatten", + "description": "Issuer Error." + }, "add_code": { "message": "Account toevoegen", "description": "Add account." @@ -43,10 +55,6 @@ "message": "Sommige accountgegevens zijn niet geïmporteerd.", "description": "Some migration data is broken." }, - "close": { - "message": "Sluiten", - "description": "Close." - }, "ok": { "message": "OK", "description": "OK." @@ -96,13 +104,9 @@ "description": "Security." }, "current_phrase": { - "message": "Huidig ​​wachtwoord", + "message": "Huidig wachtwoord", "description": "Current Passphrase." }, - "new_phrase": { - "message": "Nieuw wachtwoord", - "description": "New Passphrase." - }, "phrase": { "message": "Wachtwoord", "description": "Passphrase." @@ -120,25 +124,21 @@ "description": "Message that user is required to acknowledge before clearing all data." }, "delete_all": { - "message": "Authenticator herinitialiseren" + "message": "OTPilot resetten" }, "delete_all_warning": { - "message": "Dit zal al uw gegevens verwijderen en de Authenticator volledig herinitialiseren. U bent niet in staat om verwijderde gegevens te herstellen! Overweeg om een back-up op te slaan voordat u de Authenticator herinitialiseert." + "message": "Dit verwijdert al uw gegevens en herstelt OTPilot volledig naar de beginstaat. Verwijderde gegevens kunt u niet meer terugkrijgen! Overweeg om een back-up te maken voordat u OTPilot reset." }, "security_warning": { "message": "Dit wachtwoord wordt gebruikt voor het versleutelen van uw accounts. Niemand kan u helpen als u het wachtwoord vergeet.", "description": "Passphrase Warning." }, - "update": { - "message": "Bijwerken", - "description": "Update." - }, "phrase_incorrect": { - "message": "U kunt niet een nieuw account toevoegen tot alle accounts zijn ontcijferd. Voer het juiste wachtwoord in voordat u verder gaat.", + "message": "U kunt geen nieuw account toevoegen totdat alle accounts zijn ontsleuteld. Voer het juiste wachtwoord in voordat u verdergaat.", "description": "Passphrase Incorrect." }, "phrase_incorrect_export": { - "message": "Accounts die niet kunnen worden ontcijferd, worden niet in deze back-up opgenomen.", + "message": "Accounts die niet ontsleuteld konden worden, worden niet in deze back-up opgenomen.", "description": "Skip Unable-decripted Data." }, "phrase_not_match": { @@ -157,28 +157,20 @@ "message": "Feedback", "description": "Feedback." }, - "translate": { - "message": "Vertalen", - "description": "Translate." - }, "source": { "message": "Broncode", "description": "Source Code." }, "passphrase_info": { - "message": "Voer het wachtwoord in om de accountgegevens te ontcijferen.", + "message": "Voer het wachtwoord in om de accountgegevens te ontsleutelen.", "description": "Passphrase Info" }, "sync_clock": { "message": "Klok synchroniseren met Google", "description": "Sync Clock" }, - "remember_phrase": { - "message": "Wachtwoord onthouden", - "description": "Remember Passphrase" - }, "clock_too_far_off": { - "message": "Let op! De klok van uw lokale computer is te ver voor/achter, gelieve het te herstellen voordat u doorgaat.", + "message": "Let op! De klok van uw computer loopt te ver voor of achter. Corrigeer dit voordat u verdergaat.", "description": "Local Time is Too Far Off" }, "remind_backup": { @@ -210,7 +202,7 @@ "description": "Scale" }, "export_info": { - "message": "Waarschuwing: alle back-ups zijn niet versleuteld. Wilt u een account toevoegen aan een andere app? Beweeg over de rechterbovenkant van een account en druk op de verborgen knop.", + "message": "Waarschuwing: alle back-ups zijn niet versleuteld. Wilt u een account aan een andere app toevoegen? Beweeg de muis over de rechterbovenkant van een account en klik op de verborgen knop.", "description": "Export menu info text" }, "download_backup": { @@ -222,7 +214,7 @@ "description": "Import backup." }, "import_backup_file": { - "message": "Back-up importeren", + "message": "Back-upbestand importeren", "description": "Import backup file." }, "import_backup_qr": { @@ -234,15 +226,47 @@ "description": "Import qr images. Shown as add account method." }, "import_backup_code": { - "message": "Tekstback-up importeren ", + "message": "OTPAuth-URI importeren", "description": "Import backup code." }, "import_otp_urls": { - "message": "OTP-URL’s importeren", + "message": "OTPAuth-URI importeren", "description": "Import OTP URLs. Shown as add account method." }, + "import_drop_file": { + "message": "Sleep een back-upbestand hierheen of klik om te bladeren", + "description": "File import dropzone title" + }, + "import_file_accepts": { + "message": "Accepteert .json- en .txt-exports", + "description": "File import dropzone hint" + }, + "import_choose_file": { + "message": "Bestand kiezen", + "description": "File import dropzone button" + }, + "import_encrypted_note": { + "message": "Als het bestand versleuteld is, wordt u na het selecteren om het wachtwoord gevraagd.", + "description": "File import note about encrypted backups" + }, + "import_enc_required": { + "message": "Versleuteld — wachtwoord vereist", + "description": "Badge on a picked encrypted backup file" + }, + "import_pass_placeholder": { + "message": "Voer het wachtwoord van het bestand in", + "description": "Passphrase input placeholder for encrypted import" + }, + "import_pass_hint": { + "message": "Dit is het wachtwoord dat u hebt ingesteld toen u de back-up maakte.", + "description": "Hint under the import passphrase field" + }, + "import_decrypt": { + "message": "Ontsleutelen en importeren", + "description": "Button to decrypt and import an encrypted backup" + }, "import_backup_qr_partly_failed": { - "message": "Import succesvol, maar sommige QR-codes kunnen niet worden herkend.", + "message": "Import succesvol, maar sommige QR-codes konden niet worden herkend.", "description": "Import successful, but some QR image cannot be recognized." }, "import_backup_qr_in_batches": { @@ -250,7 +274,7 @@ "description": "You can select multiple image files to import backup in batches." }, "show_all_entries": { - "message": "Laat alle invoer zien", + "message": "Alle vermeldingen weergeven", "description": "Show all entries." }, "dropbox_risk": { @@ -262,7 +286,7 @@ "description": "Error password warning when import backups." }, "local_passphrase_warning": { - "message": "Uw wachtwoord is lokaal opgeslagen, wijzig deze direct in het menu beveiliging.", + "message": "Uw wachtwoord is lokaal opgeslagen. Wijzig dit onmiddellijk in het beveiligingsmenu.", "description": "localStorage password warning." }, "remove": { @@ -297,14 +321,106 @@ "message": "Automatisch aanvullen gebruiken", "description": "Use Autofill" }, - "use_high_contrast": { - "message": "Hoog contrast gebruiken", - "description": "Use High Contrast" - }, "theme": { "message": "Thema", "description": "Theme" }, + "settings_appearance": { + "message": "Uiterlijk", + "description": "Settings section: appearance" + }, + "settings_general": { + "message": "Algemeen", + "description": "Settings section: general" + }, + "edit_accounts": { + "message": "Accounts bewerken", + "description": "Header title while in edit mode" + }, + "show_qr": { + "message": "QR-code weergeven", + "description": "Context-menu action to show an account's QR code" + }, + "pin_to_top": { + "message": "Vastzetten bovenaan", + "description": "Context-menu action to pin an account to the top" + }, + "unpin": { + "message": "Losmaken", + "description": "Context-menu action to unpin an account" + }, + "vault_locked": { + "message": "Kluis vergrendeld", + "description": "Title on the unlock screen" + }, + "unlock": { + "message": "Ontgrendelen", + "description": "Unlock button" + }, + "qr_transfer_title": { + "message": "Dit account overzetten", + "description": "QR sheet caption title" + }, + "qr_transfer_desc": { + "message": "Scan deze code met OTPilot op een ander apparaat om dit account over te zetten — het verlaat nooit uw apparaten.", + "description": "QR sheet caption body" + }, + "backup_on_device": { + "message": "Op dit apparaat", + "description": "Backup section: local" + }, + "backup_cloud_sync": { + "message": "Cloudsynchronisatie", + "description": "Backup section: cloud" + }, + "backup_connected": { + "message": "Verbonden", + "description": "Cloud provider connected status" + }, + "backup_not_connected": { + "message": "Niet verbonden", + "description": "Cloud provider not-connected status" + }, + "showing_codes_for": { + "message": "Toont codes voor", + "description": "Smart-filter banner prefix, followed by the site domain" + }, + "other_accounts": { + "message": "Overige accounts", + "description": "Divider above non-matching accounts in the filter view" + }, + "filter_to_site": { + "message": "Filteren op", + "description": "Banner prefix to re-apply the site filter, followed by the domain" + }, + "onboarding_title": { + "message": "Codes die met u meereizen", + "description": "Onboarding headline" + }, + "onboarding_subtitle": { + "message": "Genereer veilige tweefactorcodes voor elk account — versleuteld, offline en altijd met één tik te kopiëren.", + "description": "Onboarding subtitle" + }, + "onboarding_feature_offline": { + "message": "Werkt volledig offline — niets verlaat uw apparaat", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_encrypted": { + "message": "Versleutelde kluis met optionele wachtwoordvergrendeling", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_scan": { + "message": "Scan een QR-code of importeer een bestaande back-up", + "description": "Onboarding feature bullet" + }, + "onboarding_get_started": { + "message": "Aan de slag", + "description": "Onboarding primary button" + }, + "onboarding_import": { + "message": "Ik heb een back-up om te importeren", + "description": "Onboarding secondary button" + }, "theme_light": { "message": "Licht", "description": "Light theme" @@ -313,9 +429,9 @@ "message": "Donker", "description": "Dark theme" }, - "theme_simple": { - "message": "Eenvoudig", - "description": "Simple theme" + "theme_auto": { + "message": "Automatisch", + "description": "Follow the system light/dark setting" }, "theme_compact": { "message": "Compact", @@ -325,14 +441,6 @@ "message": "Hoog contrast", "description": "High Contrast theme" }, - "theme_flat": { - "message": "Vlak", - "description": "Flat theme" - }, - "storage_sync_info": { - "message": "Automatisch een back-up maken van uw gegevens naar opslagdiensten van derden.", - "description": "3rd party backup info" - }, "browser_sync": { "message": "Browsersynchronisatie", "description": "Storage location" @@ -362,10 +470,6 @@ } } }, - "otp_unsupported_warn": { - "message": "U hebt een of meer Steam- of Blizzard-accounts. Niet-versleutelde back-ups zullen geen gestandaardiseerde backup-indeling gebruiken.", - "description": "Warning if using account that is not supported by standard backup format." - }, "otp_backup_inform": { "message": "U kunt back-ups van enkele andere toepassingen importeren.", "description": "Info text on import page" @@ -396,7 +500,7 @@ "message": "Ongeldig" }, "digits": { - "message": "Digits" + "message": "Cijfers" }, "algorithm": { "message": "Algoritme" @@ -407,9 +511,6 @@ "backup": { "message": "Back-up" }, - "backup_file_info": { - "message": "Back-up uw gegevens naar een bestand." - }, "password_policy_default_hint": { "message": "Uw wachtwoord voldoet niet aan de beveiligingseisen van uw organisatie. Neem contact op met uw beheerder voor meer informatie." }, @@ -429,7 +530,7 @@ "message": "Automatisch invullen kan worden ingeschakeld om automatisch codes op websites in te vullen." }, "advisor_insight_smart_filter_not_enabled": { - "message": "Het inschakelen van een slim filter zorgt voor snelle toegang tot accounts." + "message": "Het inschakelen van Slimme filter zorgt voor snelle toegang tot accounts." }, "show_all_insights": { "message": "Alle inzichten tonen." @@ -468,7 +569,7 @@ "message": "Niet-herroepbare toestemmingen weergeven" }, "permission_required": { - "message": "Dit is een vereiste toestemming die niet is te verwijderen." + "message": "Dit is een vereiste toestemming die niet kan worden ingetrokken." }, "permission_active_tab": { "message": "Toegang tot het huidige tabblad om QR-codes te scannen." @@ -489,7 +590,7 @@ "message": "Geeft alleen-schrijventoegang tot het klembord om codes naar het klembord te kopiëren wanneer u op de account klikt." }, "permission_context_menus": { - "message": "Voegt Authenticator toe aan contextmenu." + "message": "Voegt OTPilot toe aan contextmenu." }, "permission_sync_clock": { "message": "Tijdssynchronisatie met Google toestaan." @@ -500,18 +601,6 @@ "permission_dropbox_cannot_revoke": { "message": "U moet eerst back-up naar Dropbox uitschakelen." }, - "permission_drive": { - "message": "Back-up naar Google Drive toestaan." - }, - "permission_drive_cannot_revoke": { - "message": "U moet eerst back-up naar Google Drive uitschakelen." - }, - "permission_onedrive": { - "message": "Back-up naar OneDrive toestaan." - }, - "permission_onedrive_cannot_revoke": { - "message": "U moet eerst back-up naar OneDrive uitschakelen." - }, "permission_unknown_permission": { "message": "Onbekende toestemming. Indien dit bericht wordt weergegeven, stuur dan een bugrapport." }, @@ -520,5 +609,9 @@ }, "activate_auto_filter": { "message": "Waarschuwing: Slim filter koppelt op een vrij losse manier de domeinnaam aan een account. Controleer altijd of u op de juiste website bent voordat u een code invoert!" + }, + "backup_unavailable": { + "message": "Nog niet beschikbaar", + "description": "Shown on a cloud provider that is not implemented yet." } } diff --git a/_locales/no/messages.json b/_locales/no/messages.json index 3d7e56c1b..6ff0bf27d 100644 --- a/_locales/no/messages.json +++ b/_locales/no/messages.json @@ -1,18 +1,26 @@ { "extName": { - "message": "Autentisering", + "message": "OTPilot Authenticator", "description": "Extension Name." }, "extShortName": { - "message": "Authenticator", + "message": "OTPilot", "description": "Extension Short Name." }, + "host": { + "message": "Nettsted", + "description": "Bound website host used to restrict autofill to a matching page." + }, + "backup_requires_password": { + "message": "Angi et passord først for å bruke skysikkerhetskopiering, slik at hemmelighetene dine krypteres før de forlater denne enheten.", + "description": "Shown in the cloud backup section when no master password is set." + }, "extDesc": { - "message": "Authenticator genererer to-faktor autentiseringskoder i nettleseren din.", + "message": "OTPilot Authenticator genererer tofaktorkoder (2FA), med kryptert skysikkerhetskopiering, nettstedstilpasset autofyll og sikkerhetsråd.", "description": "Extension Description." }, "added": { - "message": " har blitt lagt til.", + "message": " er lagt til.", "description": "Added Account." }, "errorqr": { @@ -23,6 +31,10 @@ "message": "Ugyldig kontohemmelighet", "description": "Secret Error." }, + "errorissuer": { + "message": "Utsteder kan ikke inneholde «::»", + "description": "Issuer Error." + }, "add_code": { "message": "Legg til konto", "description": "Add account." @@ -43,10 +55,6 @@ "message": "Noen kontodata ble ikke importert.", "description": "Some migration data is broken." }, - "close": { - "message": "Lukk", - "description": "Close." - }, "ok": { "message": "Ok", "description": "OK." @@ -72,7 +80,7 @@ "description": "Issuer." }, "secret": { - "message": "Hemmelig", + "message": "Hemmelighet", "description": "Secret." }, "updateSuccess": { @@ -99,10 +107,6 @@ "message": "Nåværende passord", "description": "Current Passphrase." }, - "new_phrase": { - "message": "Nytt passord", - "description": "New Passphrase." - }, "phrase": { "message": "Passord", "description": "Passphrase." @@ -112,33 +116,29 @@ "description": "Confirm Passphrase." }, "confirm_delete": { - "message": "Er du sikker på at du vil slette denne brukeren? Denne handlingen kan ikke angres.", + "message": "Er du sikker på at du vil slette denne kontoen? Denne handlingen kan ikke angres.", "description": "Remove entry confirmation" }, "confirm_delete_all": { - "message": "Jeg forstår at alle mine data ikke vil bli gjenopprettet slettet.", + "message": "Jeg forstår at alle dataene mine vil bli permanent slettet og ikke kan gjenopprettes.", "description": "Message that user is required to acknowledge before clearing all data." }, "delete_all": { - "message": "Tilbakestill Authentikator" + "message": "Tilbakestill OTPilot" }, "delete_all_warning": { - "message": "Dette vil slette alle dataene dine og alle nullstille autentisering. Du vil ikke kunne gjenopprette noen slettede data! Du bør vurdere å lagre en sikkerhetskopi før du tilbakestiller autentisering." + "message": "Dette vil slette alle dataene dine og fullstendig tilbakestille OTPilot. Du vil ikke kunne gjenopprette noen slettede data! Du bør vurdere å lagre en sikkerhetskopi før du tilbakestiller OTPilot." }, "security_warning": { "message": "Dette passordet vil bli brukt til å kryptere kontoene dine. Ingen kan hjelpe deg hvis du glemmer passordet.", "description": "Passphrase Warning." }, - "update": { - "message": "Oppdater", - "description": "Update." - }, "phrase_incorrect": { "message": "Du kan ikke legge til en ny konto før alle kontoer er dekryptert. Skriv inn riktig passord før du fortsetter.", "description": "Passphrase Incorrect." }, "phrase_incorrect_export": { - "message": "Kontoer som ikke var i stand til å dekryptere vil ikke inkluderes i denne sikkerhetskopien.", + "message": "Kontoer som ikke kunne dekrypteres vil ikke inkluderes i denne sikkerhetskopien.", "description": "Skip Unable-decripted Data." }, "phrase_not_match": { @@ -157,26 +157,18 @@ "message": "Tilbakemelding", "description": "Feedback." }, - "translate": { - "message": "Oversett", - "description": "Translate." - }, "source": { "message": "Kildekode", "description": "Source Code." }, "passphrase_info": { - "message": "Skriv inn passordet for å dekryptere konto data.", + "message": "Skriv inn passordet for å dekryptere kontodata.", "description": "Passphrase Info" }, "sync_clock": { "message": "Synkroniser klokke med Google", "description": "Sync Clock" }, - "remember_phrase": { - "message": "Husk Passord", - "description": "Remember Passphrase" - }, "clock_too_far_off": { "message": "Forsiktig! Din lokale klokke er for langt unna. Vennligst fiks den før du fortsetter.", "description": "Local Time is Too Far Off" @@ -190,7 +182,7 @@ "description": "Capture Failed" }, "capture_local_file_failed": { - "message": "Prøver du å skanne QR-kode fra en lokal fil? Bruk importer QR-bildesikkerhetskopiering i stedet.", + "message": "Prøver du å skanne en QR-kode fra en lokal fil? Bruk «Importer QR-bildesikkerhetskopi» i stedet.", "description": "Import QR image backup instead of scan local image" }, "based_on_time": { @@ -218,7 +210,7 @@ "description": "Download backup file." }, "import_backup": { - "message": "Importer Sikkerhetskopi", + "message": "Importer sikkerhetskopi", "description": "Import backup." }, "import_backup_file": { @@ -226,7 +218,7 @@ "description": "Import backup file." }, "import_backup_qr": { - "message": "Importer QR Bilde Sikkerhetskopi", + "message": "Importer QR-bildesikkerhetskopi", "description": "Import qr image backup." }, "import_qr_images": { @@ -234,19 +226,51 @@ "description": "Import qr images. Shown as add account method." }, "import_backup_code": { - "message": "Importer sikkerhetskopi av tekst", + "message": "Importer OTPAuth-URI", "description": "Import backup code." }, "import_otp_urls": { - "message": "Importer URL-er for engangspassord", + "message": "Importer OTPAuth-URI", "description": "Import OTP URLs. Shown as add account method." }, + "import_drop_file": { + "message": "Slipp en sikkerhetskopifil her, eller klikk for å bla gjennom", + "description": "File import dropzone title" + }, + "import_file_accepts": { + "message": "Godtar .json- og .txt-eksporter", + "description": "File import dropzone hint" + }, + "import_choose_file": { + "message": "Velg fil", + "description": "File import dropzone button" + }, + "import_encrypted_note": { + "message": "Hvis filen er kryptert, blir du bedt om passordet etter at du har valgt den.", + "description": "File import note about encrypted backups" + }, + "import_enc_required": { + "message": "Kryptert – passord kreves", + "description": "Badge on a picked encrypted backup file" + }, + "import_pass_placeholder": { + "message": "Skriv inn filens passord", + "description": "Passphrase input placeholder for encrypted import" + }, + "import_pass_hint": { + "message": "Dette er passordet du angav da du opprettet sikkerhetskopien.", + "description": "Hint under the import passphrase field" + }, + "import_decrypt": { + "message": "Dekrypter og importer", + "description": "Button to decrypt and import an encrypted backup" + }, "import_backup_qr_partly_failed": { "message": "Import vellykket, men noen QR-koder kunne ikke gjenkjennes.", "description": "Import successful, but some QR image cannot be recognized." }, "import_backup_qr_in_batches": { - "message": "Du kan velge flere filer å importere i bunt.", + "message": "Du kan velge flere filer for å importere sikkerhetskopier i puljer.", "description": "You can select multiple image files to import backup in batches." }, "show_all_entries": { @@ -294,17 +318,109 @@ "description": "Manual backup" }, "use_autofill": { - "message": "Bruk Autofyll", + "message": "Bruk autofyll", "description": "Use Autofill" }, - "use_high_contrast": { - "message": "Bruk Høykontrast", - "description": "Use High Contrast" - }, "theme": { "message": "Tema", "description": "Theme" }, + "settings_appearance": { + "message": "Utseende", + "description": "Settings section: appearance" + }, + "settings_general": { + "message": "Generelt", + "description": "Settings section: general" + }, + "edit_accounts": { + "message": "Rediger kontoer", + "description": "Header title while in edit mode" + }, + "show_qr": { + "message": "Vis QR-kode", + "description": "Context-menu action to show an account's QR code" + }, + "pin_to_top": { + "message": "Fest øverst", + "description": "Context-menu action to pin an account to the top" + }, + "unpin": { + "message": "Løsne", + "description": "Context-menu action to unpin an account" + }, + "vault_locked": { + "message": "Hvelv låst", + "description": "Title on the unlock screen" + }, + "unlock": { + "message": "Lås opp", + "description": "Unlock button" + }, + "qr_transfer_title": { + "message": "Overfør denne kontoen", + "description": "QR sheet caption title" + }, + "qr_transfer_desc": { + "message": "Skann denne koden med OTPilot på en annen enhet for å flytte denne kontoen over – den forlater aldri enhetene dine.", + "description": "QR sheet caption body" + }, + "backup_on_device": { + "message": "På denne enheten", + "description": "Backup section: local" + }, + "backup_cloud_sync": { + "message": "Skysynkronisering", + "description": "Backup section: cloud" + }, + "backup_connected": { + "message": "Tilkoblet", + "description": "Cloud provider connected status" + }, + "backup_not_connected": { + "message": "Ikke tilkoblet", + "description": "Cloud provider not-connected status" + }, + "showing_codes_for": { + "message": "Viser koder for", + "description": "Smart-filter banner prefix, followed by the site domain" + }, + "other_accounts": { + "message": "Andre kontoer", + "description": "Divider above non-matching accounts in the filter view" + }, + "filter_to_site": { + "message": "Filtrer til", + "description": "Banner prefix to re-apply the site filter, followed by the domain" + }, + "onboarding_title": { + "message": "Koder som følger deg", + "description": "Onboarding headline" + }, + "onboarding_subtitle": { + "message": "Generer sikre tofaktorkoder for hver konto – kryptert, offline, og alltid ett trykk unna kopiering.", + "description": "Onboarding subtitle" + }, + "onboarding_feature_offline": { + "message": "Fungerer helt offline – ingenting forlater enheten din", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_encrypted": { + "message": "Kryptert hvelv med valgfri passordlås", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_scan": { + "message": "Skann en QR-kode eller importer en eksisterende sikkerhetskopi", + "description": "Onboarding feature bullet" + }, + "onboarding_get_started": { + "message": "Kom i gang", + "description": "Onboarding primary button" + }, + "onboarding_import": { + "message": "Jeg har en sikkerhetskopi å importere", + "description": "Onboarding secondary button" + }, "theme_light": { "message": "Lys", "description": "Light theme" @@ -313,9 +429,9 @@ "message": "Mørk", "description": "Dark theme" }, - "theme_simple": { - "message": "Enkel", - "description": "Simple theme" + "theme_auto": { + "message": "Automatisk", + "description": "Follow the system light/dark setting" }, "theme_compact": { "message": "Kompakt", @@ -325,16 +441,8 @@ "message": "Høy kontrast", "description": "High Contrast theme" }, - "theme_flat": { - "message": "Flat", - "description": "Flat theme" - }, - "storage_sync_info": { - "message": "Sikkerhetskopier dataene dine automatisk til tredjeparts lagringstjenester.", - "description": "3rd party backup info" - }, "browser_sync": { - "message": "Synkroniser nettlesere", + "message": "Nettlesersynkronisering", "description": "Storage location" }, "sign_in": { @@ -353,7 +461,7 @@ "description": "Sign out of 3rd party storage services" }, "token_revoked": { - "message": "Det oppsto et problem med tilkoblingen til $SERVICE$ -kontoen din, vennligst logg deg på igjen.", + "message": "Det oppsto et problem med tilkoblingen til $SERVICE$-kontoen din, vennligst logg deg på igjen.", "description": "Error authenticating to backup service. $SERVICE$ will be replaced with a proper noun (E.g.: 'Google Drive' or 'Dropbox')", "placeholders": { "service": { @@ -362,10 +470,6 @@ } } }, - "otp_unsupported_warn": { - "message": "Du har en eller flere Steam- eller Blizzard-kontoer. Ikke-krypterte sikkerhetskopier bruker ikke standardisert backup-format.", - "description": "Warning if using account that is not supported by standard backup format." - }, "otp_backup_inform": { "message": "Du kan importere sikkerhetskopier fra noen andre applikasjoner.", "description": "Info text on import page" @@ -402,14 +506,11 @@ "message": "Algoritme" }, "smart_filter": { - "message": "Smart Filter" + "message": "Smartfilter" }, "backup": { "message": "Sikkerhetskopi" }, - "backup_file_info": { - "message": "Sikkerhetskopier data til en fil." - }, "password_policy_default_hint": { "message": "Passordet ditt oppfyller ikke organisasjonens sikkerhetskrav. Kontakt systemansvarlig for mer informasjon." }, @@ -423,13 +524,13 @@ "message": "Aktiver automatisk lås for å beskytte dine data." }, "advisor_insight_browser_sync_not_enabled": { - "message": "Synkronisering i nettleseren er deaktivert. Aktivering av kontoene kan synkroniseres på tvers av nettlesere." + "message": "Nettlesersynkronisering er deaktivert. Aktiverer du den, kan kontoene synkroniseres på tvers av nettlesere." }, "advisor_insight_auto_fill_not_enabled": { - "message": "Autofyll kan aktiveres automatisk for å fylle koder inn på nettsteder." + "message": "Autofyll kan aktiveres for å fylle inn koder automatisk på nettsteder." }, "advisor_insight_smart_filter_not_enabled": { - "message": "Aktivering av smart filter gir rask tilgang til kontoer." + "message": "Aktivering av smartfilter gir rask tilgang til kontoer." }, "show_all_insights": { "message": "Vis alle innsikter." @@ -453,7 +554,7 @@ "message": "Lær mer" }, "enable_context_menu": { - "message": "Legg til i kontekst-menyen" + "message": "Legg til i kontekstmenyen" }, "no_entires": { "message": "Ingen kontoer å vise. Legg til din første konto nå." @@ -471,7 +572,7 @@ "message": "Dette er en nødvendig tillatelse og kan ikke tilbakekalles." }, "permission_active_tab": { - "message": "Få tilgang til denne fanen for å skanne QR-koder." + "message": "Tilgang til gjeldende fane for å skanne QR-koder." }, "permission_storage": { "message": "Tilgang til nettleserlagring for å lagre kontodata." @@ -483,13 +584,13 @@ "message": "Lar automatisk lås fungere." }, "permission_scripting": { - "message": "Injiser skript inn i denne fanen for å skanne QR-koder og la autoutfylle fungere." + "message": "Injiserer skript i gjeldende fane for å skanne QR-koder og la autofyll fungere." }, "permission_clipboard_write": { "message": "Gir skrivetilgang til utklippstavlen for å kopiere koder til utklippstavlen når du klikker på kontoen." }, "permission_context_menus": { - "message": "Legger til autentisering i kontekstmenyen." + "message": "Legger til OTPilot i kontekstmenyen." }, "permission_sync_clock": { "message": "Tillater klokkesynk med Google." @@ -500,18 +601,6 @@ "permission_dropbox_cannot_revoke": { "message": "Du må deaktivere Dropbox-sikkerhetskopiering først." }, - "permission_drive": { - "message": "Tillater sikkerhetskopiering til Google Drive." - }, - "permission_drive_cannot_revoke": { - "message": "Du må deaktivere Google Drive sikkerhetskopiering først." - }, - "permission_onedrive": { - "message": "Tillater sikkerhetskopiering til OneDrive." - }, - "permission_onedrive_cannot_revoke": { - "message": "Du må deaktivere OneDrive sikkerhetskopiering først." - }, "permission_unknown_permission": { "message": "Ukjent tillatelse. Hvis du ser denne meldingen, vennligst send en feilrapport." }, @@ -519,6 +608,10 @@ "message": "Feil passord" }, "activate_auto_filter": { - "message": "Advarsel: Smartfilter samsvarer ikke med domenenavnet til en konto. Kontroller alltid at du er på korrekt nettside før du skriver inn en kode!" + "message": "Advarsel: Smartfilter samsvarer løst med domenenavnet til en konto. Kontroller alltid at du er på riktig nettside før du skriver inn en kode!" + }, + "backup_unavailable": { + "message": "Ikke tilgjengelig ennå", + "description": "Shown on a cloud provider that is not implemented yet." } } diff --git a/_locales/pl/messages.json b/_locales/pl/messages.json index df2f4485d..38e0a4a78 100644 --- a/_locales/pl/messages.json +++ b/_locales/pl/messages.json @@ -1,14 +1,22 @@ { "extName": { - "message": "Authenticator\n", + "message": "OTPilot Authenticator", "description": "Extension Name." }, "extShortName": { - "message": "Uwierzytelnianie", + "message": "OTPilot", "description": "Extension Short Name." }, + "host": { + "message": "Witryna", + "description": "Bound website host used to restrict autofill to a matching page." + }, + "backup_requires_password": { + "message": "Najpierw ustaw hasło, aby móc korzystać z kopii zapasowej w chmurze — dzięki temu Twoje sekrety zostaną zaszyfrowane, zanim opuszczą to urządzenie.", + "description": "Shown in the cloud backup section when no master password is set." + }, "extDesc": { - "message": "Authenticator generuje w przeglądarce kody uwierzytelniania dwuskładnikowego.", + "message": "OTPilot Authenticator generuje kody uwierzytelniania dwuskładnikowego (2FA), oferując szyfrowaną kopię zapasową w chmurze, autouzupełnianie dopasowane do witryny oraz porady dotyczące bezpieczeństwa.", "description": "Extension Description." }, "added": { @@ -23,6 +31,10 @@ "message": "Niepoprawny sekretny kod konta", "description": "Secret Error." }, + "errorissuer": { + "message": "Nazwa wystawcy nie może zawierać znaków \"::\"", + "description": "Issuer Error." + }, "add_code": { "message": "Dodaj konto", "description": "Add account." @@ -43,10 +55,6 @@ "message": "Niektóre dane konta nie zostały pomyślnie zaimportowane.", "description": "Some migration data is broken." }, - "close": { - "message": "Zamknij", - "description": "Close." - }, "ok": { "message": "OK", "description": "OK." @@ -99,10 +107,6 @@ "message": "Obecne hasło", "description": "Current Passphrase." }, - "new_phrase": { - "message": "Nowe hasło", - "description": "New Passphrase." - }, "phrase": { "message": "Hasło", "description": "Passphrase." @@ -120,19 +124,15 @@ "description": "Message that user is required to acknowledge before clearing all data." }, "delete_all": { - "message": "Zresetuj dodatek Authenticator" + "message": "Zresetuj dodatek OTPilot" }, "delete_all_warning": { - "message": "Spowoduje to usunięcie wszystkich danych i całkowite zresetowanie dodatku Authenticator. Nie będzie można odzyskać usuniętych danych! Przed zresetowaniem dodatku Authenticator należy rozważyć zapisanie kopii zapasowej." + "message": "Spowoduje to usunięcie wszystkich danych i całkowite zresetowanie dodatku OTPilot. Nie będzie można odzyskać usuniętych danych! Przed zresetowaniem dodatku OTPilot należy rozważyć zapisanie kopii zapasowej." }, "security_warning": { "message": "To hasło będzie służyć szyfrowaniu Twoich kont. Nikt ci nie pomoże, jeśli zapomnisz hasła.", "description": "Passphrase Warning." }, - "update": { - "message": "Aktualizuj", - "description": "Update." - }, "phrase_incorrect": { "message": "Nie możesz dodać nowego konta, dopóki wszystkie konta nie zostaną odszyfrowane. Przed kontynuowaniem podaj prawidłowe hasło.", "description": "Passphrase Incorrect." @@ -157,10 +157,6 @@ "message": "Uwagi", "description": "Feedback." }, - "translate": { - "message": "Przetłumacz", - "description": "Translate." - }, "source": { "message": "Kod źródłowy", "description": "Source Code." @@ -173,10 +169,6 @@ "message": "Synchronizuj zegar z Google", "description": "Sync Clock" }, - "remember_phrase": { - "message": "Zapamiętaj hasło", - "description": "Remember Passphrase" - }, "clock_too_far_off": { "message": "Uwaga! Twój lokalny zegar jest zbyt niedokładny, należy go ustawić przed kontynuowaniem.", "description": "Local Time is Too Far Off" @@ -210,7 +202,7 @@ "description": "Scale" }, "export_info": { - "message": "Uwaga: wszystkie kopie zapasowe są odszyfrowane. Chcesz dodać kono do innej aplikacji? Ustaw kursor nad prawym górnym rogiem dowolnego konta i naciśnij ukryty przycisk.", + "message": "Uwaga: wszystkie kopie zapasowe są niezaszyfrowane. Chcesz dodać konto do innej aplikacji? Najedź kursorem na prawy górny róg dowolnego konta i naciśnij ukryty przycisk.", "description": "Export menu info text" }, "download_backup": { @@ -234,13 +226,45 @@ "description": "Import qr images. Shown as add account method." }, "import_backup_code": { - "message": "Importuj tekstową kopię zapasową", + "message": "Importuj URI OTPAuth", "description": "Import backup code." }, "import_otp_urls": { - "message": "Importuj adresy URL OTP", + "message": "Importuj URI OTPAuth", "description": "Import OTP URLs. Shown as add account method." }, + "import_drop_file": { + "message": "Upuść plik kopii zapasowej lub kliknij, aby wybrać", + "description": "File import dropzone title" + }, + "import_file_accepts": { + "message": "Obsługuje eksporty .json i .txt", + "description": "File import dropzone hint" + }, + "import_choose_file": { + "message": "Wybierz plik", + "description": "File import dropzone button" + }, + "import_encrypted_note": { + "message": "Jeśli plik jest zaszyfrowany, po jego wybraniu zostaniesz poproszony o podanie hasła.", + "description": "File import note about encrypted backups" + }, + "import_enc_required": { + "message": "Zaszyfrowane — wymagane hasło", + "description": "Badge on a picked encrypted backup file" + }, + "import_pass_placeholder": { + "message": "Wprowadź hasło do pliku", + "description": "Passphrase input placeholder for encrypted import" + }, + "import_pass_hint": { + "message": "To hasło ustawione podczas tworzenia kopii zapasowej.", + "description": "Hint under the import passphrase field" + }, + "import_decrypt": { + "message": "Odszyfruj i importuj", + "description": "Button to decrypt and import an encrypted backup" + }, "import_backup_qr_partly_failed": { "message": "Importowanie powiodło się, ale niektóre kody QR nie zostały rozpoznane.", "description": "Import successful, but some QR image cannot be recognized." @@ -258,11 +282,11 @@ "description": "Backup risk warning." }, "import_error_password": { - "message": "Należy podać prawidłowe hasło, żeby importować kopię zapasową.", + "message": "Należy podać prawidłowe hasło, żeby zaimportować kopię zapasową.", "description": "Error password warning when import backups." }, "local_passphrase_warning": { - "message": "Hasła są przechowywane lokalnie, należy to natychmiast zmienić w menu Zabezpieczenia.", + "message": "Twoje hasło jest przechowywane lokalnie, zmień je jak najszybciej w menu Zabezpieczenia.", "description": "localStorage password warning." }, "remove": { @@ -297,14 +321,106 @@ "message": "Użyj autowypełniania", "description": "Use Autofill" }, - "use_high_contrast": { - "message": "Użyj dużego kontrastu", - "description": "Use High Contrast" - }, "theme": { "message": "Motyw", "description": "Theme" }, + "settings_appearance": { + "message": "Wygląd", + "description": "Settings section: appearance" + }, + "settings_general": { + "message": "Ogólne", + "description": "Settings section: general" + }, + "edit_accounts": { + "message": "Edytuj konta", + "description": "Header title while in edit mode" + }, + "show_qr": { + "message": "Pokaż kod QR", + "description": "Context-menu action to show an account's QR code" + }, + "pin_to_top": { + "message": "Przypnij na górze", + "description": "Context-menu action to pin an account to the top" + }, + "unpin": { + "message": "Odepnij", + "description": "Context-menu action to unpin an account" + }, + "vault_locked": { + "message": "Skarbiec zablokowany", + "description": "Title on the unlock screen" + }, + "unlock": { + "message": "Odblokuj", + "description": "Unlock button" + }, + "qr_transfer_title": { + "message": "Przenieś to konto", + "description": "QR sheet caption title" + }, + "qr_transfer_desc": { + "message": "Zeskanuj ten kod za pomocą OTPilot na innym urządzeniu, aby przenieść to konto — dane nigdy nie opuszczają Twoich urządzeń.", + "description": "QR sheet caption body" + }, + "backup_on_device": { + "message": "Na tym urządzeniu", + "description": "Backup section: local" + }, + "backup_cloud_sync": { + "message": "Synchronizacja w chmurze", + "description": "Backup section: cloud" + }, + "backup_connected": { + "message": "Połączono", + "description": "Cloud provider connected status" + }, + "backup_not_connected": { + "message": "Nie połączono", + "description": "Cloud provider not-connected status" + }, + "showing_codes_for": { + "message": "Wyświetlanie kodów dla", + "description": "Smart-filter banner prefix, followed by the site domain" + }, + "other_accounts": { + "message": "Inne konta", + "description": "Divider above non-matching accounts in the filter view" + }, + "filter_to_site": { + "message": "Filtruj do", + "description": "Banner prefix to re-apply the site filter, followed by the domain" + }, + "onboarding_title": { + "message": "Kody, które są zawsze przy Tobie", + "description": "Onboarding headline" + }, + "onboarding_subtitle": { + "message": "Generuj bezpieczne kody uwierzytelniania dwuskładnikowego dla każdego konta — zaszyfrowane, offline i zawsze o jedno dotknięcie od skopiowania.", + "description": "Onboarding subtitle" + }, + "onboarding_feature_offline": { + "message": "Działa całkowicie offline — nic nie opuszcza Twojego urządzenia", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_encrypted": { + "message": "Zaszyfrowany skarbiec z opcjonalną blokadą hasłem", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_scan": { + "message": "Zeskanuj kod QR lub zaimportuj istniejącą kopię zapasową", + "description": "Onboarding feature bullet" + }, + "onboarding_get_started": { + "message": "Rozpocznij", + "description": "Onboarding primary button" + }, + "onboarding_import": { + "message": "Mam kopię zapasową do zaimportowania", + "description": "Onboarding secondary button" + }, "theme_light": { "message": "Jasny", "description": "Light theme" @@ -313,9 +429,9 @@ "message": "Ciemny", "description": "Dark theme" }, - "theme_simple": { - "message": "Prosty", - "description": "Simple theme" + "theme_auto": { + "message": "Automatyczny", + "description": "Follow the system light/dark setting" }, "theme_compact": { "message": "Kompaktowy", @@ -325,14 +441,6 @@ "message": "Wysoki kontrast", "description": "High Contrast theme" }, - "theme_flat": { - "message": "Płaski", - "description": "Flat theme" - }, - "storage_sync_info": { - "message": "Automatycznie twórz kopie zapasowe danych w usługach przechowywania innych firm.", - "description": "3rd party backup info" - }, "browser_sync": { "message": "Synchronizacja przeglądarki", "description": "Storage location" @@ -362,10 +470,6 @@ } } }, - "otp_unsupported_warn": { - "message": "Masz jedno lub więcej kont Steam lub Blizzard. Nieszyfrowane kopie zapasowe nie będą korzystać ze standardowego formatu kopii zapasowej.", - "description": "Warning if using account that is not supported by standard backup format." - }, "otp_backup_inform": { "message": "Możesz importować kopie zapasowe z innych aplikacji.", "description": "Info text on import page" @@ -407,9 +511,6 @@ "backup": { "message": "Kopia zapasowa" }, - "backup_file_info": { - "message": "Zapisz kopię zapasową danych do pliku." - }, "password_policy_default_hint": { "message": "Twoje hasło nie spełnia wymogów bezpieczeństwa Twojej organizacji. Aby uzyskać więcej informacji, skontaktuj się z administratorem." }, @@ -426,7 +527,7 @@ "message": "Synchronizacja przeglądarki jest wyłączona. Włączenie jej umożliwia synchronizację kont pomiędzy przeglądarkami." }, "advisor_insight_auto_fill_not_enabled": { - "message": "Autouzupełnianie może być włączone, aby automatycznie wypełniać kody na stronach internetowych." + "message": "Włączenie autouzupełniania pozwala automatycznie wypełniać kody na stronach internetowych." }, "advisor_insight_smart_filter_not_enabled": { "message": "Włączenie inteligentnego filtra umożliwia szybki dostęp do kont." @@ -489,7 +590,7 @@ "message": "Przyznaje dostęp tylko do zapisu do schowka w celu skopiowania kodów do schowka po kliknięciu konta." }, "permission_context_menus": { - "message": "Dodaje dodatek Authenticator do menu kontekstowego." + "message": "Dodaje dodatek OTPilot do menu kontekstowego." }, "permission_sync_clock": { "message": "Umożliwia synchronizację zegara z Google." @@ -498,19 +599,7 @@ "message": "Umożliwia tworzenie kopii zapasowej w Dropboksie." }, "permission_dropbox_cannot_revoke": { - "message": "Musisz najpierw wyłączyć kopię zapasową Dropboksa." - }, - "permission_drive": { - "message": "Umożliwia tworzenie kopii zapasowej na Dysku Google." - }, - "permission_drive_cannot_revoke": { - "message": "Musisz najpierw wyłączyć kopię zapasową Dysku Google." - }, - "permission_onedrive": { - "message": "Umożliwia tworzenie kopii zapasowej w OneDrive." - }, - "permission_onedrive_cannot_revoke": { - "message": "Musisz najpierw wyłączyć kopię zapasową OneDrive." + "message": "Musisz najpierw wyłączyć kopię zapasową w Dropboksie." }, "permission_unknown_permission": { "message": "Nieznane uprawnienie. Jeśli widzisz ten komunikat, wyślij raport o błędzie." @@ -520,5 +609,9 @@ }, "activate_auto_filter": { "message": "Ostrzeżenie: inteligentny filtr luźno dopasowuje nazwę domeny do konta. Zawsze sprawdzaj, czy jesteś na właściwej witrynie internetowej przed wprowadzeniem kodu!" + }, + "backup_unavailable": { + "message": "Jeszcze niedostępne", + "description": "Shown on a cloud provider that is not implemented yet." } } diff --git a/_locales/pt/messages.json b/_locales/pt/messages.json index 9cc98065f..4ac0c2da9 100644 --- a/_locales/pt/messages.json +++ b/_locales/pt/messages.json @@ -1,18 +1,26 @@ { "extName": { - "message": "Autenticador", + "message": "OTPilot Authenticator", "description": "Extension Name." }, "extShortName": { - "message": "Autenticador", + "message": "OTPilot", "description": "Extension Short Name." }, + "host": { + "message": "Site", + "description": "Bound website host used to restrict autofill to a matching page." + }, + "backup_requires_password": { + "message": "Defina uma palavra-passe primeiro para utilizar a cópia de segurança na nuvem, para que os seus segredos sejam encriptados antes de saírem deste dispositivo.", + "description": "Shown in the cloud backup section when no master password is set." + }, "extDesc": { - "message": "O autenticador gera códigos de autenticação de dois factores no seu navegador.", + "message": "O OTPilot Authenticator gera códigos de dois fatores (2FA), com cópia de segurança na nuvem encriptada, preenchimento automático vinculado ao site e recomendações de segurança.", "description": "Extension Description." }, "added": { - "message": "foi adicionado ao.", + "message": " foi adicionado.", "description": "Added Account." }, "errorqr": { @@ -20,9 +28,13 @@ "description": "QR Error." }, "errorsecret": { - "message": "Invalid account secret", + "message": "Segredo da conta inválido", "description": "Secret Error." }, + "errorissuer": { + "message": "O emissor não pode conter \"::\"", + "description": "Issuer Error." + }, "add_code": { "message": "Adicionar conta", "description": "Add account." @@ -36,17 +48,13 @@ "description": "Manual Entry." }, "migration_fail": { - "message": "Import failed. If you are migrating data from Google Authenticator, please re-export your data from Google Authenticator and try again.", + "message": "Falha na importação. Se estiver a migrar dados do Google Authenticator, reexporte os seus dados do Google Authenticator e tente novamente.", "description": "Import migration data failed." }, "migration_partly_fail": { - "message": "Some account data was not imported successfully.", + "message": "Alguns dados de contas não foram importados com sucesso.", "description": "Some migration data is broken." }, - "close": { - "message": "Fechar", - "description": "Close." - }, "ok": { "message": "OK", "description": "OK." @@ -80,7 +88,7 @@ "description": "Update Success." }, "updateFailure": { - "message": "Falhou.", + "message": "Falha.", "description": "Update Failure." }, "about": { @@ -99,10 +107,6 @@ "message": "Palavra-passe atual", "description": "Current Passphrase." }, - "new_phrase": { - "message": "Nova palavra-passe", - "description": "New Passphrase." - }, "phrase": { "message": "Palavra-passe", "description": "Passphrase." @@ -120,19 +124,15 @@ "description": "Message that user is required to acknowledge before clearing all data." }, "delete_all": { - "message": "Redefinir Autenticação" + "message": "Redefinir OTPilot" }, "delete_all_warning": { - "message": "Irá apagar todos os seus dados e redefinir a autenticação completamente. Não será possível recuperar quaisquer dados apagados! Deve considerar guardar uma cópia de segurança antes de redefinir a autenticação." + "message": "Irá apagar todos os seus dados e redefinir o OTPilot completamente. Não será possível recuperar quaisquer dados apagados! Deve considerar guardar uma cópia de segurança antes de redefinir o OTPilot." }, "security_warning": { "message": "Esta palavra-passe será utilizada para encriptar todas as suas contas. Ninguém poderá ajudá-lo caso se esqueça da sua palavra-passe.", "description": "Passphrase Warning." }, - "update": { - "message": "Atualizar", - "description": "Update." - }, "phrase_incorrect": { "message": "Não pode adicionar uma nova conta até que todas as contas estejam desencriptadas. Por favor, digite a palavra-passe correta antes de continuar.", "description": "Passphrase Incorrect." @@ -157,10 +157,6 @@ "message": "Feedback", "description": "Feedback." }, - "translate": { - "message": "Traduzir", - "description": "Translate." - }, "source": { "message": "Código fonte", "description": "Source Code." @@ -173,12 +169,8 @@ "message": "Sincronizar relógio com Google", "description": "Sync Clock" }, - "remember_phrase": { - "message": "Lembrar palavra-passe", - "description": "Remember Passphrase" - }, "clock_too_far_off": { - "message": "Cuidado! O seu relógio está atrasado, por favor corrija antes de continuar.", + "message": "Cuidado! O seu relógio está muito dessincronizado, corrija-o antes de continuar.", "description": "Local Time is Too Far Off" }, "remind_backup": { @@ -190,7 +182,7 @@ "description": "Capture Failed" }, "capture_local_file_failed": { - "message": "Are you trying to scan QR code from a local file? Use Import QR Image Backup instead.", + "message": "Está a tentar ler um código QR a partir de um ficheiro local? Utilize antes a opção \"Importar código QR\".", "description": "Import QR image backup instead of scan local image" }, "based_on_time": { @@ -222,7 +214,7 @@ "description": "Import backup." }, "import_backup_file": { - "message": "Importar cópia de segurança", + "message": "Importar ficheiro de cópia de segurança", "description": "Import backup file." }, "import_backup_qr": { @@ -230,19 +222,51 @@ "description": "Import qr image backup." }, "import_qr_images": { - "message": "Import QR Images", + "message": "Importar imagens de código QR", "description": "Import qr images. Shown as add account method." }, "import_backup_code": { - "message": "Importar código de segurança", + "message": "Importar URI OTPAuth", "description": "Import backup code." }, "import_otp_urls": { - "message": "Import OTP URLs", + "message": "Importar URI OTPAuth", "description": "Import OTP URLs. Shown as add account method." }, + "import_drop_file": { + "message": "Arraste um ficheiro de cópia de segurança ou clique para procurar", + "description": "File import dropzone title" + }, + "import_file_accepts": { + "message": "Aceita exportações .json e .txt", + "description": "File import dropzone hint" + }, + "import_choose_file": { + "message": "Escolher ficheiro", + "description": "File import dropzone button" + }, + "import_encrypted_note": { + "message": "Se o ficheiro estiver encriptado, ser-lhe-á pedida a palavra-passe após selecioná-lo.", + "description": "File import note about encrypted backups" + }, + "import_enc_required": { + "message": "Encriptado — palavra-passe necessária", + "description": "Badge on a picked encrypted backup file" + }, + "import_pass_placeholder": { + "message": "Introduza a palavra-passe do ficheiro", + "description": "Passphrase input placeholder for encrypted import" + }, + "import_pass_hint": { + "message": "Esta é a palavra-passe que definiu quando criou a cópia de segurança.", + "description": "Hint under the import passphrase field" + }, + "import_decrypt": { + "message": "Desencriptar e importar", + "description": "Button to decrypt and import an encrypted backup" + }, "import_backup_qr_partly_failed": { - "message": "Import successful, but some QR codes could not be recognized.", + "message": "Importação concluída, mas alguns códigos QR não foram reconhecidos.", "description": "Import successful, but some QR image cannot be recognized." }, "import_backup_qr_in_batches": { @@ -290,21 +314,113 @@ "description": "Edit" }, "manual_dropbox": { - "message": "Manual Backup", + "message": "Cópia de segurança manual", "description": "Manual backup" }, "use_autofill": { "message": "Utilizar preenchimento automático", "description": "Use Autofill" }, - "use_high_contrast": { - "message": "Utilizar alto contraste", - "description": "Use High Contrast" - }, "theme": { "message": "Tema", "description": "Theme" }, + "settings_appearance": { + "message": "Aparência", + "description": "Settings section: appearance" + }, + "settings_general": { + "message": "Geral", + "description": "Settings section: general" + }, + "edit_accounts": { + "message": "Editar contas", + "description": "Header title while in edit mode" + }, + "show_qr": { + "message": "Mostrar código QR", + "description": "Context-menu action to show an account's QR code" + }, + "pin_to_top": { + "message": "Fixar no topo", + "description": "Context-menu action to pin an account to the top" + }, + "unpin": { + "message": "Desafixar", + "description": "Context-menu action to unpin an account" + }, + "vault_locked": { + "message": "Cofre bloqueado", + "description": "Title on the unlock screen" + }, + "unlock": { + "message": "Desbloquear", + "description": "Unlock button" + }, + "qr_transfer_title": { + "message": "Transferir esta conta", + "description": "QR sheet caption title" + }, + "qr_transfer_desc": { + "message": "Leia este código com o OTPilot noutro dispositivo para transferir esta conta — nunca sai dos seus dispositivos.", + "description": "QR sheet caption body" + }, + "backup_on_device": { + "message": "Neste dispositivo", + "description": "Backup section: local" + }, + "backup_cloud_sync": { + "message": "Sincronização na nuvem", + "description": "Backup section: cloud" + }, + "backup_connected": { + "message": "Ligado", + "description": "Cloud provider connected status" + }, + "backup_not_connected": { + "message": "Não ligado", + "description": "Cloud provider not-connected status" + }, + "showing_codes_for": { + "message": "A mostrar códigos para", + "description": "Smart-filter banner prefix, followed by the site domain" + }, + "other_accounts": { + "message": "Outras contas", + "description": "Divider above non-matching accounts in the filter view" + }, + "filter_to_site": { + "message": "Filtrar para", + "description": "Banner prefix to re-apply the site filter, followed by the domain" + }, + "onboarding_title": { + "message": "Códigos que viajam consigo", + "description": "Onboarding headline" + }, + "onboarding_subtitle": { + "message": "Gere códigos de dois fatores seguros para cada conta — encriptados, offline e sempre a um toque de copiar.", + "description": "Onboarding subtitle" + }, + "onboarding_feature_offline": { + "message": "Funciona totalmente offline — nada sai do seu dispositivo", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_encrypted": { + "message": "Cofre encriptado com bloqueio por palavra-passe opcional", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_scan": { + "message": "Leia um código QR ou importe uma cópia de segurança existente", + "description": "Onboarding feature bullet" + }, + "onboarding_get_started": { + "message": "Começar", + "description": "Onboarding primary button" + }, + "onboarding_import": { + "message": "Tenho uma cópia de segurança para importar", + "description": "Onboarding secondary button" + }, "theme_light": { "message": "Claro", "description": "Light theme" @@ -313,28 +429,20 @@ "message": "Escuro", "description": "Dark theme" }, - "theme_simple": { - "message": "Simple", - "description": "Simple theme" + "theme_auto": { + "message": "Automático", + "description": "Follow the system light/dark setting" }, "theme_compact": { - "message": "Compact", + "message": "Compacto", "description": "Compact theme" }, "theme_high_contrast": { "message": "Alto Contraste", "description": "High Contrast theme" }, - "theme_flat": { - "message": "Flat", - "description": "Flat theme" - }, - "storage_sync_info": { - "message": "Automaticamente os seus dados serão exportados para serviços de armazenamento de terceiros.", - "description": "3rd party backup info" - }, "browser_sync": { - "message": "Browser Sync", + "message": "Sincronização do navegador", "description": "Storage location" }, "sign_in": { @@ -342,18 +450,18 @@ "description": "Sign in to 3rd party storage services" }, "sign_in_business": { - "message": "Sign in (Business)", + "message": "Entrar (Empresa)", "description": "Sign in to 3rd party storage services" }, "onedrive_business_perms": { - "message": "Why do business accounts require more permissions?" + "message": "Porque é que as contas empresariais precisam de mais permissões?" }, "log_out": { "message": "Sair", "description": "Sign out of 3rd party storage services" }, "token_revoked": { - "message": "Houve um problema na conexão com sua conta do $SERVICE$, por favor faça o login novamente.", + "message": "Houve um problema ao ligar à sua conta do $SERVICE$, por favor entre novamente.", "description": "Error authenticating to backup service. $SERVICE$ will be replaced with a proper noun (E.g.: 'Google Drive' or 'Dropbox')", "placeholders": { "service": { @@ -362,12 +470,8 @@ } } }, - "otp_unsupported_warn": { - "message": "Você tem uma ou mais contas Steam ou Blizzard. As cópias de segurança não encriptadas não utilizarão o formato padrão de backup.", - "description": "Warning if using account that is not supported by standard backup format." - }, "otp_backup_inform": { - "message": "Você pode importar cópias de segurança de outras aplicações.", + "message": "Pode importar cópias de segurança de outras aplicações.", "description": "Info text on import page" }, "otp_backup_learn": { @@ -405,120 +509,109 @@ "message": "Filtro Inteligente" }, "backup": { - "message": "Backup" - }, - "backup_file_info": { - "message": "Backup your data to a file." + "message": "Cópia de segurança" }, "password_policy_default_hint": { - "message": "Your password does not meet your organization's security requirements. Contact your administrator for more information." + "message": "A sua palavra-passe não cumpre os requisitos de segurança da sua organização. Contacte o administrador para mais informações." }, "advisor": { - "message": "Advisor" + "message": "Consultor" }, "advisor_insight_password_not_set": { - "message": "Set a password to protect your data." + "message": "Defina uma palavra-passe para proteger os seus dados." }, "advisor_insight_auto_lock_not_set": { - "message": "Enable auto-lock to protect your data." + "message": "Ative o bloqueio automático para proteger os seus dados." }, "advisor_insight_browser_sync_not_enabled": { - "message": "Browser sync is disabled. Enabling it allows accounts to be synced across browsers." + "message": "A sincronização do navegador está desativada. Ativá-la permite sincronizar as contas entre navegadores." }, "advisor_insight_auto_fill_not_enabled": { - "message": "Autofill can be enabled to automatically fill codes into websites." + "message": "O preenchimento automático pode ser ativado para preencher os códigos automaticamente nos sites." }, "advisor_insight_smart_filter_not_enabled": { - "message": "Enabling smart filter allows for quick access to accounts." + "message": "Ativar o filtro inteligente permite um acesso rápido às contas." }, "show_all_insights": { - "message": "Show all insights." + "message": "Mostrar todas as recomendações." }, "no_insight_available": { - "message": "No insights found, everything looks good!" + "message": "Não foram encontradas recomendações, está tudo em ordem!" }, "danger": { - "message": "Danger" + "message": "Perigo" }, "warning": { - "message": "Warning" + "message": "Aviso" }, "info": { - "message": "Info" + "message": "Informação" }, "dismiss": { - "message": "Dismiss" + "message": "Dispensar" }, "learn_more": { - "message": "Learn more" + "message": "Saber mais" }, "enable_context_menu": { - "message": "Add to context menu" + "message": "Adicionar ao menu de contexto" }, "no_entires": { - "message": "No accounts to display. Add your first account now." + "message": "Não existem contas para mostrar. Adicione já a sua primeira conta." }, "permissions": { - "message": "Permissions" + "message": "Permissões" }, "permission_revoke": { - "message": "Revoke" + "message": "Revogar" }, "permission_show_required_permissions": { - "message": "Show non-revocable permissions" + "message": "Mostrar permissões não revogáveis" }, "permission_required": { - "message": "This is a required permission and cannot be revoked." + "message": "Esta é uma permissão obrigatória e não pode ser revogada." }, "permission_active_tab": { - "message": "Access to the current tab to scan QR codes." + "message": "Acesso ao separador atual para ler códigos QR." }, "permission_storage": { - "message": "Access to browser storage to store account data." + "message": "Acesso ao armazenamento do navegador para guardar os dados das contas." }, "permission_identity": { - "message": "Allows sign in to 3rd party storage services." + "message": "Permite entrar em serviços de armazenamento de terceiros." }, "permission_alarms": { - "message": "Allows auto-lock to work." + "message": "Permite o funcionamento do bloqueio automático." }, "permission_scripting": { - "message": "Inject scripts into he current tab to scan QR codes and allow auto-fill to work." + "message": "Injeta scripts no separador atual para ler códigos QR e permitir o funcionamento do preenchimento automático." }, "permission_clipboard_write": { - "message": "Grants write-only access to the clipboard to copy codes to clipboard when you click on the account." + "message": "Concede acesso apenas de escrita à área de transferência para copiar os códigos quando clica numa conta." }, "permission_context_menus": { - "message": "Adds Authenticator to context menu." + "message": "Adiciona o OTPilot ao menu de contexto." }, "permission_sync_clock": { - "message": "Allows clock sync with Google." + "message": "Permite sincronizar o relógio com Google." }, "permission_dropbox": { - "message": "Allows backup to Dropbox." + "message": "Permite cópia de segurança para o Dropbox." }, "permission_dropbox_cannot_revoke": { - "message": "You must disable Dropbox backup first." - }, - "permission_drive": { - "message": "Allows backup to Google Drive." - }, - "permission_drive_cannot_revoke": { - "message": "You must disable Google Drive backup first." - }, - "permission_onedrive": { - "message": "Allows backup to OneDrive." - }, - "permission_onedrive_cannot_revoke": { - "message": "You must disable OneDrive backup first." + "message": "Tem de desativar primeiro a cópia de segurança do Dropbox." }, "permission_unknown_permission": { - "message": "Unknown permission. If see this message, please send a bug report." + "message": "Permissão desconhecida. Se vir esta mensagem, envie um relatório de erro." }, "phrase_wrong": { - "message": "Password incorrect" + "message": "Palavra-passe incorreta" }, "activate_auto_filter": { - "message": "Warning: Smart filter loosely matches the domain name to an account. Always verify that you are on the correct website before entering a code!" + "message": "Aviso: o filtro inteligente associa o domínio a uma conta de forma aproximada. Verifique sempre que está no site correto antes de introduzir um código!" + }, + "backup_unavailable": { + "message": "Ainda não disponível", + "description": "Shown on a cloud provider that is not implemented yet." } } diff --git a/_locales/pt_BR/messages.json b/_locales/pt_BR/messages.json index 938af5cbd..77f9c2f55 100644 --- a/_locales/pt_BR/messages.json +++ b/_locales/pt_BR/messages.json @@ -1,14 +1,22 @@ { "extName": { - "message": "Autenticador", + "message": "OTPilot Authenticator", "description": "Extension Name." }, "extShortName": { - "message": "Autenticador", + "message": "OTPilot", "description": "Extension Short Name." }, + "host": { + "message": "Site", + "description": "Bound website host used to restrict autofill to a matching page." + }, + "backup_requires_password": { + "message": "Defina uma senha primeiro para usar o backup na nuvem, assim seus segredos são criptografados antes de saírem deste dispositivo.", + "description": "Shown in the cloud backup section when no master password is set." + }, "extDesc": { - "message": "O autenticador gera códigos de verificação em dois fatores no seu navegador.", + "message": "O OTPilot Authenticator gera códigos de dois fatores (2FA), com backup criptografado na nuvem, preenchimento automático vinculado ao site e recomendações de segurança.", "description": "Extension Description." }, "added": { @@ -23,12 +31,16 @@ "message": "Segredo da conta inválido", "description": "Secret Error." }, + "errorissuer": { + "message": "O emissor não pode conter \"::\"", + "description": "Issuer Error." + }, "add_code": { "message": "Adicionar conta", "description": "Add account." }, "add_qr": { - "message": "Ler código QR", + "message": "Escanear código QR", "description": "Scan QR Code." }, "add_secret": { @@ -36,17 +48,13 @@ "description": "Manual Entry." }, "migration_fail": { - "message": "Falha na importação. Se você está migrando dados do Google Authenticator, por favor re-exporte seus dados do Google Authenticator e tente novamente.", + "message": "Falha na importação. Se você está migrando dados do Google Authenticator, por favor reexporte seus dados do Google Authenticator e tente novamente.", "description": "Import migration data failed." }, "migration_partly_fail": { "message": "Alguns dados da conta não foram importados com sucesso.", "description": "Some migration data is broken." }, - "close": { - "message": "Fechar", - "description": "Close." - }, "ok": { "message": "OK", "description": "OK." @@ -99,10 +107,6 @@ "message": "Senha Atual", "description": "Current Passphrase." }, - "new_phrase": { - "message": "Nova Senha", - "description": "New Passphrase." - }, "phrase": { "message": "Senha", "description": "Passphrase." @@ -116,23 +120,19 @@ "description": "Remove entry confirmation" }, "confirm_delete_all": { - "message": "Eu compreendo que todos os meus dados serão irrecuperavelmente deletados.", + "message": "Eu entendo que todos os meus dados serão excluídos permanentemente e não poderão ser recuperados.", "description": "Message that user is required to acknowledge before clearing all data." }, "delete_all": { - "message": "Redefinir Autenticador" + "message": "Redefinir OTPilot" }, "delete_all_warning": { - "message": "Isso irá deletar todos os seus dados e redefinir o Autenticador completamente. Você não conseguirá recuperar qualquer dado deletado! Você deveria fazer uma cópia de segurança antes de redefinir o Autenticador." + "message": "Isso irá deletar todos os seus dados e redefinir o OTPilot completamente. Você não conseguirá recuperar qualquer dado deletado! Você deveria fazer uma cópia de segurança antes de redefinir o OTPilot." }, "security_warning": { "message": "Essa senha será usada para criptografar as suas contas. Ninguém poderá te ajudar se você esquecer a senha.", "description": "Passphrase Warning." }, - "update": { - "message": "Atualizar", - "description": "Update." - }, "phrase_incorrect": { "message": "Você não pode adicionar uma nova conta até que todas as contas estejam descriptografadas. Por favor digite a senha correta antes de continuar.", "description": "Passphrase Incorrect." @@ -154,15 +154,11 @@ "description": "Copied." }, "feedback": { - "message": "Dê sua opinião", + "message": "Feedback", "description": "Feedback." }, - "translate": { - "message": "Traduzir", - "description": "Translate." - }, "source": { - "message": "Código Fonte", + "message": "Código-fonte", "description": "Source Code." }, "passphrase_info": { @@ -173,12 +169,8 @@ "message": "Sincronizar Relógio com o Google", "description": "Sync Clock" }, - "remember_phrase": { - "message": "Lembrar Senha", - "description": "Remember Passphrase" - }, "clock_too_far_off": { - "message": "Cuidado! Seu relógio local está parado há muito tempo, por favor ajuste-o antes de continuar.", + "message": "Cuidado! Seu relógio local está muito dessincronizado, por favor ajuste-o antes de continuar.", "description": "Local Time is Too Far Off" }, "remind_backup": { @@ -210,7 +202,7 @@ "description": "Scale" }, "export_info": { - "message": "Aviso: todos os backups estão descriptografados. Deseja adicionar uma conta a outro aplicativo? Passe o mouse sobre a parte superior direita de qualquer conta e aperte o botão oculto.", + "message": "Aviso: todos os backups não estão criptografados. Deseja adicionar uma conta a outro aplicativo? Passe o mouse sobre a parte superior direita de qualquer conta e aperte o botão oculto.", "description": "Export menu info text" }, "download_backup": { @@ -234,13 +226,45 @@ "description": "Import qr images. Shown as add account method." }, "import_backup_code": { - "message": "Importar backup em texto", + "message": "Importar URI OTPAuth", "description": "Import backup code." }, "import_otp_urls": { - "message": "Importar URLs OTP", + "message": "Importar URI OTPAuth", "description": "Import OTP URLs. Shown as add account method." }, + "import_drop_file": { + "message": "Solte um arquivo de backup ou clique para procurar", + "description": "File import dropzone title" + }, + "import_file_accepts": { + "message": "Aceita exportações .json e .txt", + "description": "File import dropzone hint" + }, + "import_choose_file": { + "message": "Escolher arquivo", + "description": "File import dropzone button" + }, + "import_encrypted_note": { + "message": "Se o arquivo estiver criptografado, a senha dele será solicitada após selecioná-lo.", + "description": "File import note about encrypted backups" + }, + "import_enc_required": { + "message": "Criptografado — senha necessária", + "description": "Badge on a picked encrypted backup file" + }, + "import_pass_placeholder": { + "message": "Digite a senha do arquivo", + "description": "Passphrase input placeholder for encrypted import" + }, + "import_pass_hint": { + "message": "Esta é a senha que você definiu ao criar o backup.", + "description": "Hint under the import passphrase field" + }, + "import_decrypt": { + "message": "Descriptografar e importar", + "description": "Button to decrypt and import an encrypted backup" + }, "import_backup_qr_partly_failed": { "message": "Importado com sucesso, mas alguns códigos QR não puderam ser reconhecidos.", "description": "Import successful, but some QR image cannot be recognized." @@ -254,7 +278,7 @@ "description": "Show all entries." }, "dropbox_risk": { - "message": "Aviso: os backups estão descriptografados. Use a seu próprio risco.", + "message": "Aviso: os backups não estão criptografados. Use por sua conta e risco.", "description": "Backup risk warning." }, "import_error_password": { @@ -274,7 +298,7 @@ "description": "Download Encrypted Backup" }, "search": { - "message": "Busca", + "message": "Pesquisar", "description": "Search" }, "popout": { @@ -297,14 +321,106 @@ "message": "Usar o preenchimento automático", "description": "Use Autofill" }, - "use_high_contrast": { - "message": "Usar alto contraste", - "description": "Use High Contrast" - }, "theme": { "message": "Tema", "description": "Theme" }, + "settings_appearance": { + "message": "Aparência", + "description": "Settings section: appearance" + }, + "settings_general": { + "message": "Geral", + "description": "Settings section: general" + }, + "edit_accounts": { + "message": "Editar contas", + "description": "Header title while in edit mode" + }, + "show_qr": { + "message": "Mostrar código QR", + "description": "Context-menu action to show an account's QR code" + }, + "pin_to_top": { + "message": "Fixar no topo", + "description": "Context-menu action to pin an account to the top" + }, + "unpin": { + "message": "Desafixar", + "description": "Context-menu action to unpin an account" + }, + "vault_locked": { + "message": "Cofre bloqueado", + "description": "Title on the unlock screen" + }, + "unlock": { + "message": "Desbloquear", + "description": "Unlock button" + }, + "qr_transfer_title": { + "message": "Transferir esta conta", + "description": "QR sheet caption title" + }, + "qr_transfer_desc": { + "message": "Escaneie este código com o OTPilot em outro dispositivo para transferir esta conta — ela nunca sai dos seus dispositivos.", + "description": "QR sheet caption body" + }, + "backup_on_device": { + "message": "Neste dispositivo", + "description": "Backup section: local" + }, + "backup_cloud_sync": { + "message": "Sincronização na nuvem", + "description": "Backup section: cloud" + }, + "backup_connected": { + "message": "Conectado", + "description": "Cloud provider connected status" + }, + "backup_not_connected": { + "message": "Não conectado", + "description": "Cloud provider not-connected status" + }, + "showing_codes_for": { + "message": "Mostrando códigos para", + "description": "Smart-filter banner prefix, followed by the site domain" + }, + "other_accounts": { + "message": "Outras contas", + "description": "Divider above non-matching accounts in the filter view" + }, + "filter_to_site": { + "message": "Filtrar por", + "description": "Banner prefix to re-apply the site filter, followed by the domain" + }, + "onboarding_title": { + "message": "Códigos que viajam com você", + "description": "Onboarding headline" + }, + "onboarding_subtitle": { + "message": "Gere códigos de dois fatores seguros para cada conta — criptografados, off-line e sempre a um toque de distância para copiar.", + "description": "Onboarding subtitle" + }, + "onboarding_feature_offline": { + "message": "Funciona totalmente off-line — nada sai do seu dispositivo", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_encrypted": { + "message": "Cofre criptografado com bloqueio por senha opcional", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_scan": { + "message": "Escaneie um QR ou importe um backup existente", + "description": "Onboarding feature bullet" + }, + "onboarding_get_started": { + "message": "Começar", + "description": "Onboarding primary button" + }, + "onboarding_import": { + "message": "Tenho um backup para importar", + "description": "Onboarding secondary button" + }, "theme_light": { "message": "Claro", "description": "Light theme" @@ -313,9 +429,9 @@ "message": "Escuro", "description": "Dark theme" }, - "theme_simple": { - "message": "Simples", - "description": "Simple theme" + "theme_auto": { + "message": "Automático", + "description": "Follow the system light/dark setting" }, "theme_compact": { "message": "Compacto", @@ -325,20 +441,12 @@ "message": "Alto contraste", "description": "High Contrast theme" }, - "theme_flat": { - "message": "Plano", - "description": "Flat theme" - }, - "storage_sync_info": { - "message": "Automaticamente fazer backup dos seus dados para serviços de armazenamento de terceiros.", - "description": "3rd party backup info" - }, "browser_sync": { "message": "Sincronização do Navegador", "description": "Storage location" }, "sign_in": { - "message": "Iniciar sessão", + "message": "Entrar", "description": "Sign in to 3rd party storage services" }, "sign_in_business": { @@ -353,7 +461,7 @@ "description": "Sign out of 3rd party storage services" }, "token_revoked": { - "message": "Houve um problema na conexão com sua conta do $SERVICE$, por favor tente novamente.", + "message": "Houve um problema para conectar à sua conta do $SERVICE$, por favor faça login novamente.", "description": "Error authenticating to backup service. $SERVICE$ will be replaced with a proper noun (E.g.: 'Google Drive' or 'Dropbox')", "placeholders": { "service": { @@ -362,10 +470,6 @@ } } }, - "otp_unsupported_warn": { - "message": "Você tem uma ou mais contas Steam ou Blizzard. Os backups não criptografados não usarão o formato padronizado de backup.", - "description": "Warning if using account that is not supported by standard backup format." - }, "otp_backup_inform": { "message": "Você pode importar os backups de alguns outros aplicativos.", "description": "Info text on import page" @@ -407,23 +511,20 @@ "backup": { "message": "Backup" }, - "backup_file_info": { - "message": "Faça backup dos seus dados para um arquivo." - }, "password_policy_default_hint": { "message": "Sua senha não atende aos requisitos de segurança de sua organização. Contate seu administrador para mais informações." }, "advisor": { - "message": "Assessor" + "message": "Consultor" }, "advisor_insight_password_not_set": { "message": "Defina uma senha para proteger seus dados." }, "advisor_insight_auto_lock_not_set": { - "message": "Ativar bloqueio automático para proteger seus dados." + "message": "Ative o bloqueio automático para proteger seus dados." }, "advisor_insight_browser_sync_not_enabled": { - "message": "A sincronização do navegador está desativada. Ativá-lo permite que as contas sejam sincronizadas entre os navegadores." + "message": "A sincronização do navegador está desativada. Ativá-la permite que as contas sejam sincronizadas entre os navegadores." }, "advisor_insight_auto_fill_not_enabled": { "message": "O preenchimento automático pode ser ativado para preencher automaticamente os códigos nos sites." @@ -477,19 +578,19 @@ "message": "Acesso ao armazenamento do navegador para armazenar os dados da conta." }, "permission_identity": { - "message": "Permite iniciar sessão em serviços de armazenamento de terceiros." + "message": "Permite entrar em serviços de armazenamento de terceiros." }, "permission_alarms": { "message": "Permitir o bloqueio automático." }, "permission_scripting": { - "message": "Injetar códigos na aba atual para escanear códigos QR e permitir o preenchimento automático." + "message": "Injetar scripts na aba atual para escanear códigos QR e permitir o preenchimento automático." }, "permission_clipboard_write": { "message": "Concede acesso somente de escrita à área de transferência para copiar códigos para a área de transferência quando você clicar na conta." }, "permission_context_menus": { - "message": "Adiciona o Autenticador ao menu de contexto." + "message": "Adiciona o OTPilot ao menu de contexto." }, "permission_sync_clock": { "message": "Permite a sincronização de relógio com o Google." @@ -500,18 +601,6 @@ "permission_dropbox_cannot_revoke": { "message": "Você deve desativar o backup do Dropbox primeiro." }, - "permission_drive": { - "message": "Permite backup para o Google Drive." - }, - "permission_drive_cannot_revoke": { - "message": "Você deve desativar o backup do Google Drive primeiro." - }, - "permission_onedrive": { - "message": "Permite backup para o OneDrive." - }, - "permission_onedrive_cannot_revoke": { - "message": "Você deve desativar o backup do OneDrive primeiro." - }, "permission_unknown_permission": { "message": "Permissão desconhecida. Se você ver esta mensagem, por favor envie um relatório de erros." }, @@ -519,6 +608,10 @@ "message": "Senha incorreta" }, "activate_auto_filter": { - "message": "Aviso: O filtro inteligente corresponde pouco ao nome de domínio de uma conta. Verifique sempre se você está no site correto antes de inserir um código!" + "message": "Aviso: O filtro inteligente faz uma correspondência aproximada entre o domínio e a conta. Verifique sempre se você está no site correto antes de inserir um código!" + }, + "backup_unavailable": { + "message": "Ainda não disponível", + "description": "Shown on a cloud provider that is not implemented yet." } } diff --git a/_locales/ro/messages.json b/_locales/ro/messages.json index 8eb8cafbe..29696ff5b 100644 --- a/_locales/ro/messages.json +++ b/_locales/ro/messages.json @@ -1,14 +1,22 @@ { "extName": { - "message": "Authenticator", + "message": "OTPilot Authenticator", "description": "Extension Name." }, "extShortName": { - "message": "Authenticator", + "message": "OTPilot", "description": "Extension Short Name." }, + "host": { + "message": "Site web", + "description": "Bound website host used to restrict autofill to a matching page." + }, + "backup_requires_password": { + "message": "Setează mai întâi o parolă pentru a folosi backup-ul în cloud, astfel încât cheile tale secrete să fie criptate înainte de a părăsi acest dispozitiv.", + "description": "Shown in the cloud backup section when no master password is set." + }, "extDesc": { - "message": "Authenticator generează coduri de verificare pentru al doilea pas în browserul tău.", + "message": "OTPilot Authenticator generează coduri de autentificare în doi pași (2FA), cu copie de siguranță criptată în cloud, autocompletare potrivită cu site-ul și recomandări de securitate.", "description": "Extension Description." }, "added": { @@ -23,6 +31,10 @@ "message": "Cheia secretă a contului este invalidă", "description": "Secret Error." }, + "errorissuer": { + "message": "Emitentul nu poate conține \"::\"", + "description": "Issuer Error." + }, "add_code": { "message": "Adăugare cont", "description": "Add account." @@ -43,10 +55,6 @@ "message": "Unele date ale contului nu au fost importate corect.", "description": "Some migration data is broken." }, - "close": { - "message": "Închide", - "description": "Close." - }, "ok": { "message": "Ok", "description": "OK." @@ -99,10 +107,6 @@ "message": "Parola curentă", "description": "Current Passphrase." }, - "new_phrase": { - "message": "Parolă nouă", - "description": "New Passphrase." - }, "phrase": { "message": "Parolă", "description": "Passphrase." @@ -120,19 +124,15 @@ "description": "Message that user is required to acknowledge before clearing all data." }, "delete_all": { - "message": "Resetează Autentificator" + "message": "Resetează OTPilot" }, "delete_all_warning": { - "message": "Această acțiune va șterge toate datele și va reseta complet Autentificatorul. Nu veți putea recupera datele șterse! Ar fi bine să salvați o copie de rezervă înainte de a reseta autentificatorul." + "message": "Această acțiune va șterge toate datele și va reseta complet OTPilot. Nu veți putea recupera datele șterse! Ar fi bine să salvați o copie de siguranță înainte de a reseta OTPilot." }, "security_warning": { "message": "Această parolă va fi utilizată pentru criptarea conturilor tale. Dacă uiți această parolă, datele nu pot fi recuperate.", "description": "Passphrase Warning." }, - "update": { - "message": "Actualizează", - "description": "Update." - }, "phrase_incorrect": { "message": "Nu poți adăuga un nou cont înainte ca toate conturile să fie decriptate. Te rog introdu parola corectă înainte de a continua.", "description": "Passphrase Incorrect." @@ -154,13 +154,9 @@ "description": "Copied." }, "feedback": { - "message": "Opinii", + "message": "Feedback", "description": "Feedback." }, - "translate": { - "message": "Traducere", - "description": "Translate." - }, "source": { "message": "Cod sursă", "description": "Source Code." @@ -173,10 +169,6 @@ "message": "Sincronizează ceasul cu Google", "description": "Sync Clock" }, - "remember_phrase": { - "message": "Memorează parola", - "description": "Remember Passphrase" - }, "clock_too_far_off": { "message": "Atenție! Ceasul local are o abatere foarte mare. Te rugăm să remediezi problema înainte de a continua.", "description": "Local Time is Too Far Off" @@ -226,27 +218,59 @@ "description": "Import backup file." }, "import_backup_qr": { - "message": "Importă copie de rezervă a imaginii QR", + "message": "Importă copie de siguranță a imaginii QR", "description": "Import qr image backup." }, "import_qr_images": { - "message": "Import QR Images", + "message": "Importă imagini QR", "description": "Import qr images. Shown as add account method." }, "import_backup_code": { - "message": "Importă copie de siguranță sub formă de text", + "message": "Importă URI OTPAuth", "description": "Import backup code." }, "import_otp_urls": { - "message": "Import OTP URLs", + "message": "Importă URI OTPAuth", "description": "Import OTP URLs. Shown as add account method." }, + "import_drop_file": { + "message": "Trage un fișier de backup aici sau apasă pentru a răsfoi", + "description": "File import dropzone title" + }, + "import_file_accepts": { + "message": "Acceptă exporturi .json și .txt", + "description": "File import dropzone hint" + }, + "import_choose_file": { + "message": "Alege fișier", + "description": "File import dropzone button" + }, + "import_encrypted_note": { + "message": "Dacă fișierul este criptat, ți se va cere parola după ce îl selectezi.", + "description": "File import note about encrypted backups" + }, + "import_enc_required": { + "message": "Criptat — este necesară parola", + "description": "Badge on a picked encrypted backup file" + }, + "import_pass_placeholder": { + "message": "Introdu parola fișierului", + "description": "Passphrase input placeholder for encrypted import" + }, + "import_pass_hint": { + "message": "Aceasta este parola pe care ai stabilit-o când ai creat copia de siguranță.", + "description": "Hint under the import passphrase field" + }, + "import_decrypt": { + "message": "Decriptează și importă", + "description": "Button to decrypt and import an encrypted backup" + }, "import_backup_qr_partly_failed": { "message": "Importul a reușit, dar unele coduri QR nu au putut fi recunoscute.", "description": "Import successful, but some QR image cannot be recognized." }, "import_backup_qr_in_batches": { - "message": "Puteți selecta mai multe fișiere pentru a importa calupuri de copii de rezervă.", + "message": "Poți selecta mai multe fișiere pentru a importa copii de siguranță în loturi.", "description": "You can select multiple image files to import backup in batches." }, "show_all_entries": { @@ -254,7 +278,7 @@ "description": "Show all entries." }, "dropbox_risk": { - "message": "Avetisment: copiile de siguranță nu sunt criptate. Le folosești pe riscul propriu.", + "message": "Avertisment: copiile de siguranță nu sunt criptate. Le folosești pe riscul propriu.", "description": "Backup risk warning." }, "import_error_password": { @@ -282,7 +306,7 @@ "description": "Make window turn into persistent popup" }, "lock": { - "message": "Blochează acces", + "message": "Blochează", "description": "Lock accounts" }, "edit": { @@ -290,21 +314,113 @@ "description": "Edit" }, "manual_dropbox": { - "message": "Backup Manual", + "message": "Copie de siguranță manuală", "description": "Manual backup" }, "use_autofill": { - "message": "Folosește Autocompletarea", + "message": "Folosește autocompletarea", "description": "Use Autofill" }, - "use_high_contrast": { - "message": "Folosește contrast sporit", - "description": "Use High Contrast" - }, "theme": { "message": "Temă", "description": "Theme" }, + "settings_appearance": { + "message": "Aspect", + "description": "Settings section: appearance" + }, + "settings_general": { + "message": "General", + "description": "Settings section: general" + }, + "edit_accounts": { + "message": "Editează conturile", + "description": "Header title while in edit mode" + }, + "show_qr": { + "message": "Arată codul QR", + "description": "Context-menu action to show an account's QR code" + }, + "pin_to_top": { + "message": "Fixează sus", + "description": "Context-menu action to pin an account to the top" + }, + "unpin": { + "message": "Anulează fixarea", + "description": "Context-menu action to unpin an account" + }, + "vault_locked": { + "message": "Seif blocat", + "description": "Title on the unlock screen" + }, + "unlock": { + "message": "Deblochează", + "description": "Unlock button" + }, + "qr_transfer_title": { + "message": "Transferă acest cont", + "description": "QR sheet caption title" + }, + "qr_transfer_desc": { + "message": "Scanează acest cod cu OTPilot pe alt dispozitiv pentru a transfera acest cont — nu părăsește niciodată dispozitivele tale.", + "description": "QR sheet caption body" + }, + "backup_on_device": { + "message": "Pe acest dispozitiv", + "description": "Backup section: local" + }, + "backup_cloud_sync": { + "message": "Sincronizare în cloud", + "description": "Backup section: cloud" + }, + "backup_connected": { + "message": "Conectat", + "description": "Cloud provider connected status" + }, + "backup_not_connected": { + "message": "Neconectat", + "description": "Cloud provider not-connected status" + }, + "showing_codes_for": { + "message": "Se afișează codurile pentru", + "description": "Smart-filter banner prefix, followed by the site domain" + }, + "other_accounts": { + "message": "Alte conturi", + "description": "Divider above non-matching accounts in the filter view" + }, + "filter_to_site": { + "message": "Filtrează după", + "description": "Banner prefix to re-apply the site filter, followed by the domain" + }, + "onboarding_title": { + "message": "Coduri care te însoțesc oriunde", + "description": "Onboarding headline" + }, + "onboarding_subtitle": { + "message": "Generează coduri sigure de autentificare în doi pași pentru fiecare cont — criptate, offline și mereu la o atingere distanță de a fi copiate.", + "description": "Onboarding subtitle" + }, + "onboarding_feature_offline": { + "message": "Funcționează complet offline — nimic nu părăsește dispozitivul tău", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_encrypted": { + "message": "Seif criptat, cu blocare opțională prin parolă", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_scan": { + "message": "Scanează un cod QR sau importă o copie de siguranță existentă", + "description": "Onboarding feature bullet" + }, + "onboarding_get_started": { + "message": "Începe", + "description": "Onboarding primary button" + }, + "onboarding_import": { + "message": "Am o copie de siguranță de importat", + "description": "Onboarding secondary button" + }, "theme_light": { "message": "Luminoasă", "description": "Light theme" @@ -313,26 +429,18 @@ "message": "Întunecată", "description": "Dark theme" }, - "theme_simple": { - "message": "simplă", - "description": "Simple theme" + "theme_auto": { + "message": "Automată", + "description": "Follow the system light/dark setting" }, "theme_compact": { - "message": "Compact", + "message": "Compactă", "description": "Compact theme" }, "theme_high_contrast": { - "message": "Contrast sporit", + "message": "Contrast ridicat", "description": "High Contrast theme" }, - "theme_flat": { - "message": "Flat", - "description": "Flat theme" - }, - "storage_sync_info": { - "message": "Creează copii de siguranță automate folosind servicii de stocare de la terți.", - "description": "3rd party backup info" - }, "browser_sync": { "message": "Sincronizare date browser", "description": "Storage location" @@ -362,10 +470,6 @@ } } }, - "otp_unsupported_warn": { - "message": "Ai unul sau mai multe conturi Steam ori Blizzard. Copiile de siguranță necriptate nu vor putea utiliza formatul standardizat.", - "description": "Warning if using account that is not supported by standard backup format." - }, "otp_backup_inform": { "message": "Poți importa copii de siguranță din câteva alte aplicații.", "description": "Info text on import page" @@ -405,10 +509,7 @@ "message": "Filtru inteligent" }, "backup": { - "message": "Copie de rezervă" - }, - "backup_file_info": { - "message": "Salvați datele într-un fișier." + "message": "Copie de siguranță" }, "password_policy_default_hint": { "message": "Parola dvs. nu îndeplinește cerințele de securitate ale organizației. Contactați administratorul pentru mai multe informații." @@ -432,10 +533,10 @@ "message": "Activarea filtrului inteligent permite accesul rapid la conturi." }, "show_all_insights": { - "message": "Afișați toate informațiile." + "message": "Arată toate recomandările." }, "no_insight_available": { - "message": "Nu s-au găsit perspective, totul arată bine!" + "message": "Nu au fost găsite recomandări, totul arată bine!" }, "danger": { "message": "Pericol" @@ -447,10 +548,10 @@ "message": "Informații" }, "dismiss": { - "message": "Renunță" + "message": "Ignoră" }, "learn_more": { - "message": "Aflați mai multe" + "message": "Află mai multe" }, "enable_context_menu": { "message": "Adaugă la meniul contextual" @@ -468,7 +569,7 @@ "message": "Afișați permisiunile nerevocabile" }, "permission_required": { - "message": "Aceasta este o autorizație obligatorie și nu poate fi revocată." + "message": "Aceasta este o permisiune obligatorie și nu poate fi revocată." }, "permission_active_tab": { "message": "Acces la fila curentă pentru a scana codurile QR." @@ -480,45 +581,37 @@ "message": "Permite conectarea la serviciile de stocare ale unor terțe părți." }, "permission_alarms": { - "message": "Allows auto-lock to work." + "message": "Permite funcționarea blocării automate." }, "permission_scripting": { - "message": "Inject scripts into he current tab to scan QR codes and allow auto-fill to work." + "message": "Injectează scripturi în fila curentă pentru a scana codurile QR și pentru a permite funcționarea autocompletării." }, "permission_clipboard_write": { "message": "Oferă acces doar la scriere în clipboard pentru a copia coduri în clipboard atunci când faceți clic pe cont." }, "permission_context_menus": { - "message": "Adds Authenticator to context menu." + "message": "Adaugă OTPilot la meniul contextual." }, "permission_sync_clock": { - "message": "Allows clock sync with Google." + "message": "Permite sincronizarea ceasului cu Google." }, "permission_dropbox": { - "message": "Auto Backup pe Dropbox." + "message": "Permite realizarea copiilor de siguranță pe Dropbox." }, "permission_dropbox_cannot_revoke": { - "message": "Ti duhet të çaktivizosh backup-in e Google Drive si fillim." - }, - "permission_drive": { - "message": "Lejon backup drejt Google Drive." - }, - "permission_drive_cannot_revoke": { - "message": "Ti duhet të çaktivizosh backup-in e Google Drive si fillim." - }, - "permission_onedrive": { - "message": "Allows backup to OneDrive." - }, - "permission_onedrive_cannot_revoke": { - "message": "You must disable OneDrive backup first." + "message": "Trebuie mai întâi să dezactivezi backup-ul pe Dropbox." }, "permission_unknown_permission": { - "message": "Unknown permission. If see this message, please send a bug report." + "message": "Permisiune necunoscută. Dacă vezi acest mesaj, te rugăm să trimiți un raport de eroare." }, "phrase_wrong": { - "message": "Password incorrect" + "message": "Parolă incorectă" }, "activate_auto_filter": { - "message": "Warning: Smart filter loosely matches the domain name to an account. Always verify that you are on the correct website before entering a code!" + "message": "Avertisment: Filtrul inteligent asociază aproximativ numele domeniului cu un cont. Verifică mereu că te afli pe site-ul corect înainte de a introduce un cod!" + }, + "backup_unavailable": { + "message": "Nu este disponibil încă", + "description": "Shown on a cloud provider that is not implemented yet." } } diff --git a/_locales/ru/messages.json b/_locales/ru/messages.json index e226d08b7..737a61412 100644 --- a/_locales/ru/messages.json +++ b/_locales/ru/messages.json @@ -1,18 +1,26 @@ { "extName": { - "message": "Аутентификатор", + "message": "OTPilot Authenticator", "description": "Extension Name." }, "extShortName": { - "message": "Аутентификатор", + "message": "OTPilot", "description": "Extension Short Name." }, + "host": { + "message": "Веб-сайт", + "description": "Bound website host used to restrict autofill to a matching page." + }, + "backup_requires_password": { + "message": "Сначала задайте пароль, чтобы использовать облачное резервное копирование — тогда ваши секреты будут зашифрованы ещё до того, как покинут это устройство.", + "description": "Shown in the cloud backup section when no master password is set." + }, "extDesc": { - "message": "Аутентификатор генерирует коды двухфакторной аутентификации в вашем браузере.", + "message": "OTPilot Authenticator создаёт коды двухфакторной аутентификации (2FA), с зашифрованным резервным копированием в облако, автозаполнением по сайту и рекомендациями по безопасности.", "description": "Extension Description." }, "added": { - "message": " Был добавлен.", + "message": " добавлен.", "description": "Added Account." }, "errorqr": { @@ -20,9 +28,13 @@ "description": "QR Error." }, "errorsecret": { - "message": "Недопустимое имя учётной записи.", + "message": "Недопустимый секрет учётной записи", "description": "Secret Error." }, + "errorissuer": { + "message": "Издатель не может содержать «::»", + "description": "Issuer Error." + }, "add_code": { "message": "Добавить учётную запись", "description": "Add account." @@ -43,12 +55,8 @@ "message": "Не удалось импортировать часть данных аккаунта.", "description": "Some migration data is broken." }, - "close": { - "message": "Закрыть", - "description": "Close." - }, "ok": { - "message": "Готово", + "message": "ОК", "description": "OK." }, "yes": { @@ -60,7 +68,7 @@ "description": "No." }, "account": { - "message": "Учетная запись", + "message": "Учётная запись", "description": "Account." }, "accountName": { @@ -99,20 +107,16 @@ "message": "Текущий пароль", "description": "Current Passphrase." }, - "new_phrase": { - "message": "Новый Пароль", - "description": "New Passphrase." - }, "phrase": { "message": "Пароль", "description": "Passphrase." }, "confirm_phrase": { - "message": "Подтвердить Пароль", + "message": "Подтверждение пароля", "description": "Confirm Passphrase." }, "confirm_delete": { - "message": "Вы уверены, что хотите удалить эту учетную запись? Это действие нельзя отменить.", + "message": "Вы уверены, что хотите удалить эту учётную запись? Это действие нельзя отменить.", "description": "Remove entry confirmation" }, "confirm_delete_all": { @@ -120,33 +124,29 @@ "description": "Message that user is required to acknowledge before clearing all data." }, "delete_all": { - "message": "Сбросить ключ идентификации." + "message": "Сбросить OTPilot" }, "delete_all_warning": { - "message": "Это удалит все ваши данные и полностью сбросит Authenticator. Вы не сможете восстановить любые удаленные данные! Вам следует сохранить резервную копию перед сбросом аутентификатора." + "message": "Это удалит все ваши данные и полностью сбросит OTPilot. Вы не сможете восстановить какие-либо удалённые данные! Вам следует сохранить резервную копию перед сбросом OTPilot." }, "security_warning": { - "message": "Этот пароль будет использоваться для шифрования ваших учетных записей. Никто не может помочь вам, если вы забыли пароль.", + "message": "Этот пароль будет использоваться для шифрования ваших учётных записей. Никто не может помочь вам, если вы забыли пароль.", "description": "Passphrase Warning." }, - "update": { - "message": "Обновить", - "description": "Update." - }, "phrase_incorrect": { - "message": "Вы не можете добавить аккаунт, пока все аккаунты не расшифрованы. Введите правильный пароль, чтобы продолжить.", + "message": "Вы не можете добавить новую учётную запись, пока не расшифрованы все учётные записи. Введите правильный пароль, чтобы продолжить.", "description": "Passphrase Incorrect." }, "phrase_incorrect_export": { - "message": "Учетные записи, которые не были расшифрованы, не будут включены в эту резервную копию.", + "message": "Учётные записи, которые не были расшифрованы, не будут включены в эту резервную копию.", "description": "Skip Unable-decripted Data." }, "phrase_not_match": { - "message": "Не совпадает парольная фраза.", + "message": "Пароли не совпадают.", "description": "Passphrase Not Match." }, "encrypted": { - "message": "Данные зашифрованы", + "message": "Зашифровано", "description": "Encrypted." }, "copied": { @@ -157,32 +157,24 @@ "message": "Отзыв", "description": "Feedback." }, - "translate": { - "message": "Перевод", - "description": "Translate." - }, "source": { "message": "Исходный код", "description": "Source Code." }, "passphrase_info": { - "message": "Введите пароль для расшифровки данных учетной записи.", + "message": "Введите пароль для расшифровки данных учётной записи.", "description": "Passphrase Info" }, "sync_clock": { "message": "Синхронизировать время с Google", "description": "Sync Clock" }, - "remember_phrase": { - "message": "Запомнить пароль", - "description": "Remember Passphrase" - }, "clock_too_far_off": { - "message": "Внимание! Ваши данные местного времени не совпадают, пожалуйста синхронизируйте время перед тем как продолжить.", + "message": "Внимание! Часы на вашем устройстве сильно рассинхронизированы, пожалуйста, синхронизируйте время, прежде чем продолжить.", "description": "Local Time is Too Far Off" }, "remind_backup": { - "message": "У вас есть резервная копия для ваших учетных записей? Создайте резервную копию, пока не поздно!", + "message": "У вас есть резервная копия для ваших учётных записей? Создайте резервную копию, пока не поздно!", "description": "Remind Backup" }, "capture_failed": { @@ -190,15 +182,15 @@ "description": "Capture Failed" }, "capture_local_file_failed": { - "message": "Сканируете QR код из файла? Используйте команду Импортировать QR из резервного файла.", + "message": "Пытаетесь отсканировать QR-код из локального файла? Используйте вместо этого «Импорт резервной копии QR-изображений».", "description": "Import QR image backup instead of scan local image" }, "based_on_time": { - "message": "Время", + "message": "По времени", "description": "Time Based" }, "based_on_counter": { - "message": "Счетчик", + "message": "По счётчику", "description": "Counter Based" }, "resize_popup_page": { @@ -210,7 +202,7 @@ "description": "Scale" }, "export_info": { - "message": "Предупреждение: все резервные копии не зашифрованы. Хотите добавить учетную запись в другое приложение? Наведите курсор на верхнюю правую часть любой учетной записи и нажмите скрытую кнопку с QR кодом", + "message": "Предупреждение: все резервные копии не зашифрованы. Хотите добавить учётную запись в другое приложение? Наведите курсор на верхнюю правую часть любой учётной записи и нажмите скрытую кнопку.", "description": "Export menu info text" }, "download_backup": { @@ -218,7 +210,7 @@ "description": "Download backup file." }, "import_backup": { - "message": "Восстановить копию.", + "message": "Восстановить резервную копию", "description": "Import backup." }, "import_backup_file": { @@ -226,27 +218,59 @@ "description": "Import backup file." }, "import_backup_qr": { - "message": "Импортировать резервную копию QR изображений", + "message": "Импортировать резервную копию QR-изображений", "description": "Import qr image backup." }, "import_qr_images": { - "message": "Import QR Images", + "message": "Импортировать QR-изображения", "description": "Import qr images. Shown as add account method." }, "import_backup_code": { - "message": "Восстановить из текста.", + "message": "Импортировать OTPAuth URI", "description": "Import backup code." }, "import_otp_urls": { - "message": "Import OTP URLs", + "message": "Импортировать OTPAuth URI", "description": "Import OTP URLs. Shown as add account method." }, + "import_drop_file": { + "message": "Перетащите файл резервной копии сюда или нажмите, чтобы выбрать", + "description": "File import dropzone title" + }, + "import_file_accepts": { + "message": "Поддерживаются файлы экспорта .json и .txt", + "description": "File import dropzone hint" + }, + "import_choose_file": { + "message": "Выбрать файл", + "description": "File import dropzone button" + }, + "import_encrypted_note": { + "message": "Если файл зашифрован, после выбора у вас запросят пароль от него.", + "description": "File import note about encrypted backups" + }, + "import_enc_required": { + "message": "Зашифровано — требуется пароль", + "description": "Badge on a picked encrypted backup file" + }, + "import_pass_placeholder": { + "message": "Введите пароль от файла", + "description": "Passphrase input placeholder for encrypted import" + }, + "import_pass_hint": { + "message": "Это пароль, который вы задали при создании резервной копии.", + "description": "Hint under the import passphrase field" + }, + "import_decrypt": { + "message": "Расшифровать и импортировать", + "description": "Button to decrypt and import an encrypted backup" + }, "import_backup_qr_partly_failed": { - "message": "Импорт успешный, но некоторые QR-коды не могут быть распознаны.", + "message": "Импорт выполнен успешно, но некоторые QR-коды не удалось распознать.", "description": "Import successful, but some QR image cannot be recognized." }, "import_backup_qr_in_batches": { - "message": "Вы можете выбрать несколько файлов изображения для импорта резервных копий в пакетном режиме.", + "message": "Вы можете выбрать несколько файлов изображений для импорта резервных копий в пакетном режиме.", "description": "You can select multiple image files to import backup in batches." }, "show_all_entries": { @@ -262,7 +286,7 @@ "description": "Error password warning when import backups." }, "local_passphrase_warning": { - "message": "Ваши данные хоронятся в открытом виде, для защиты установите пароль в настройках безопасности.", + "message": "Ваш пароль хранится локально в незашифрованном виде, пожалуйста, немедленно смените его в меню безопасности.", "description": "localStorage password warning." }, "remove": { @@ -270,7 +294,7 @@ "description": "Remove password." }, "download_enc_backup": { - "message": "Сохранить защищенную паролем резервную копию", + "message": "Скачать защищённую паролем резервную копию", "description": "Download Encrypted Backup" }, "search": { @@ -282,7 +306,7 @@ "description": "Make window turn into persistent popup" }, "lock": { - "message": "Защищено паролем", + "message": "Заблокировать", "description": "Lock accounts" }, "edit": { @@ -297,14 +321,106 @@ "message": "Автозаполнение", "description": "Use Autofill" }, - "use_high_contrast": { - "message": "Высокая контрастность", - "description": "Use High Contrast" - }, "theme": { "message": "Тема", "description": "Theme" }, + "settings_appearance": { + "message": "Внешний вид", + "description": "Settings section: appearance" + }, + "settings_general": { + "message": "Общие", + "description": "Settings section: general" + }, + "edit_accounts": { + "message": "Редактирование учётных записей", + "description": "Header title while in edit mode" + }, + "show_qr": { + "message": "Показать QR-код", + "description": "Context-menu action to show an account's QR code" + }, + "pin_to_top": { + "message": "Закрепить сверху", + "description": "Context-menu action to pin an account to the top" + }, + "unpin": { + "message": "Открепить", + "description": "Context-menu action to unpin an account" + }, + "vault_locked": { + "message": "Хранилище заблокировано", + "description": "Title on the unlock screen" + }, + "unlock": { + "message": "Разблокировать", + "description": "Unlock button" + }, + "qr_transfer_title": { + "message": "Перенести эту учётную запись", + "description": "QR sheet caption title" + }, + "qr_transfer_desc": { + "message": "Отсканируйте этот код с помощью OTPilot на другом устройстве, чтобы перенести эту учётную запись — данные никогда не покидают ваши устройства.", + "description": "QR sheet caption body" + }, + "backup_on_device": { + "message": "На этом устройстве", + "description": "Backup section: local" + }, + "backup_cloud_sync": { + "message": "Облачная синхронизация", + "description": "Backup section: cloud" + }, + "backup_connected": { + "message": "Подключено", + "description": "Cloud provider connected status" + }, + "backup_not_connected": { + "message": "Не подключено", + "description": "Cloud provider not-connected status" + }, + "showing_codes_for": { + "message": "Показаны коды для", + "description": "Smart-filter banner prefix, followed by the site domain" + }, + "other_accounts": { + "message": "Другие учётные записи", + "description": "Divider above non-matching accounts in the filter view" + }, + "filter_to_site": { + "message": "Фильтр по", + "description": "Banner prefix to re-apply the site filter, followed by the domain" + }, + "onboarding_title": { + "message": "Коды, которые всегда с вами", + "description": "Onboarding headline" + }, + "onboarding_subtitle": { + "message": "Создавайте надёжные коды двухфакторной аутентификации для любой учётной записи — зашифрованные, автономные и всегда в одно касание от копирования.", + "description": "Onboarding subtitle" + }, + "onboarding_feature_offline": { + "message": "Полностью работает офлайн — ничего не покидает ваше устройство", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_encrypted": { + "message": "Зашифрованное хранилище с опциональной защитой паролем", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_scan": { + "message": "Отсканируйте QR-код или импортируйте существующую резервную копию", + "description": "Onboarding feature bullet" + }, + "onboarding_get_started": { + "message": "Начать", + "description": "Onboarding primary button" + }, + "onboarding_import": { + "message": "У меня есть резервная копия для импорта", + "description": "Onboarding secondary button" + }, "theme_light": { "message": "Светлая", "description": "Light theme" @@ -313,28 +429,20 @@ "message": "Тёмная", "description": "Dark theme" }, - "theme_simple": { - "message": "Простая", - "description": "Simple theme" + "theme_auto": { + "message": "Авто", + "description": "Follow the system light/dark setting" }, "theme_compact": { - "message": "Компактный", + "message": "Компактная", "description": "Compact theme" }, "theme_high_contrast": { - "message": "Высокая контрастность", + "message": "Высококонтрастная", "description": "High Contrast theme" }, - "theme_flat": { - "message": "Плоская", - "description": "Flat theme" - }, - "storage_sync_info": { - "message": "Автоматическое резервное копирование данных в сторонние службы хранения.", - "description": "3rd party backup info" - }, "browser_sync": { - "message": "Browser Sync", + "message": "Синхронизация браузера", "description": "Storage location" }, "sign_in": { @@ -353,7 +461,7 @@ "description": "Sign out of 3rd party storage services" }, "token_revoked": { - "message": "При подключении к вашей учетной записи $SERVICE$ произошла ошибка. Пожалуйста, войдите снова.", + "message": "При подключении к вашей учётной записи $SERVICE$ произошла ошибка. Пожалуйста, войдите снова.", "description": "Error authenticating to backup service. $SERVICE$ will be replaced with a proper noun (E.g.: 'Google Drive' or 'Dropbox')", "placeholders": { "service": { @@ -362,10 +470,6 @@ } } }, - "otp_unsupported_warn": { - "message": "У вас есть один или несколько аккаунтов Steam или Blizzard. Не зашифрованные резервные копии не будут использовать стандартный формат резервного копирования.", - "description": "Warning if using account that is not supported by standard backup format." - }, "otp_backup_inform": { "message": "Вы можете импортировать резервные копии из других приложений.", "description": "Info text on import page" @@ -390,10 +494,10 @@ "message": "Период" }, "type": { - "message": "Тип\n" + "message": "Тип" }, "invalid": { - "message": "Больной" + "message": "Недействительно" }, "digits": { "message": "Цифры" @@ -402,16 +506,13 @@ "message": "Алгоритм" }, "smart_filter": { - "message": "\"Умный\" фильтр" + "message": "Умный фильтр" }, "backup": { - "message": "Резервная копия" - }, - "backup_file_info": { - "message": "Backup your data to a file." + "message": "Резервное копирование" }, "password_policy_default_hint": { - "message": "Ваш пароль не соответствует требованием безопасности вашей организации. Свяжитесь с вашим администратором для получения дополнительной информации." + "message": "Ваш пароль не соответствует требованиям безопасности вашей организации. Свяжитесь с вашим администратором для получения дополнительной информации." }, "advisor": { "message": "Советник" @@ -423,19 +524,19 @@ "message": "Включите автоблокировку для защиты своих данных." }, "advisor_insight_browser_sync_not_enabled": { - "message": "Синхронизация браузера отключена. Включение этой опции позволяет синхронизировать учетные записи между браузерами." + "message": "Синхронизация браузера отключена. Включение этой опции позволяет синхронизировать учётные записи между браузерами." }, "advisor_insight_auto_fill_not_enabled": { "message": "Автозаполнение может быть включено для автоматического заполнения кодов на веб-сайтах." }, "advisor_insight_smart_filter_not_enabled": { - "message": "Включение смарт фильтра позволяет быстро получить доступ к аккаунтам." + "message": "Включение умного фильтра позволяет быстро получить доступ к учётным записям." }, "show_all_insights": { - "message": "Показать все действия." + "message": "Показать все рекомендации." }, "no_insight_available": { - "message": "Никаких действий не найдено, всё хорошо!" + "message": "Рекомендаций не найдено — всё в порядке!" }, "danger": { "message": "Опасность" @@ -444,10 +545,10 @@ "message": "Предупреждение" }, "info": { - "message": "ℹ️ Информация" + "message": "Информация" }, "dismiss": { - "message": "Пропустить" + "message": "Скрыть" }, "learn_more": { "message": "Узнать больше" @@ -456,40 +557,40 @@ "message": "Добавить в контекстное меню" }, "no_entires": { - "message": "Нет аккаунтов для отображения. Добавьте свой первый аккаунт сейчас." + "message": "Нет учётных записей для отображения. Добавьте свою первую учётную запись прямо сейчас." }, "permissions": { "message": "Разрешения" }, "permission_revoke": { - "message": "Отменить" + "message": "Отозвать" }, "permission_show_required_permissions": { - "message": "Показать неотменимые разрешения" + "message": "Показать разрешения, которые нельзя отозвать" }, "permission_required": { - "message": "Это требуемое разрешение и не может быть отменено." + "message": "Это обязательное разрешение, его нельзя отозвать." }, "permission_active_tab": { "message": "Доступ к текущей вкладке для сканирования QR-кодов." }, "permission_storage": { - "message": "Доступ к хранилищу браузера для хранения данных учетной записи." + "message": "Доступ к хранилищу браузера для хранения данных учётной записи." }, "permission_identity": { "message": "Позволяет войти в сторонние службы хранения." }, "permission_alarms": { - "message": "Разрешить автоблокировку." + "message": "Позволяет работать автоблокировке." }, "permission_scripting": { - "message": "Вставляйте скрипты в текущую вкладку для сканирования QR-кодов и автоматического заполнения для работы." + "message": "Внедряет скрипты в текущую вкладку для сканирования QR-кодов и обеспечивает работу автозаполнения." }, "permission_clipboard_write": { - "message": "Даёт доступ только для записи в буфер обмена для копирования кодов в буфер обмена, когда вы нажмете на аккаунт." + "message": "Предоставляет доступ только на запись в буфер обмена, чтобы копировать коды при нажатии на учётную запись." }, "permission_context_menus": { - "message": "Добавляет аутентификатор в меню контекста." + "message": "Добавляет OTPilot в контекстное меню." }, "permission_sync_clock": { "message": "Позволяет синхронизировать часы с Google." @@ -498,27 +599,19 @@ "message": "Позволяет делать резервную копию в Dropbox." }, "permission_dropbox_cannot_revoke": { - "message": "Сначала необходимо отключить резервную копию Dropbox." - }, - "permission_drive": { - "message": "Позволяет делать резервную копию на Google Drive." - }, - "permission_drive_cannot_revoke": { - "message": "Сначала необходимо отключить резервную копию Google Drive." - }, - "permission_onedrive": { - "message": "Позволяет делать резервную копию в OneDrive." - }, - "permission_onedrive_cannot_revoke": { - "message": "Сначала необходимо отключить резервную копию OneDrive." + "message": "Сначала необходимо отключить резервное копирование в Dropbox." }, "permission_unknown_permission": { - "message": "Неизвестное разрешение. Если вы видите это сообщение, отправьте сообщение об ошибке." + "message": "Неизвестное разрешение. Если вы видите это сообщение, отправьте отчёт об ошибке." }, "phrase_wrong": { "message": "Неверный пароль" }, "activate_auto_filter": { - "message": "Внимание: Интеллектуальный фильтр точно сопоставляет доменное имя с учетной записью. Всегда проверяйте, что вы находитесь на правильном веб-сайте, прежде чем вводить код!" + "message": "Внимание: умный фильтр лишь приблизительно сопоставляет доменное имя с учётной записью. Всегда проверяйте, что вы находитесь на правильном веб-сайте, прежде чем вводить код!" + }, + "backup_unavailable": { + "message": "Пока недоступно", + "description": "Shown on a cloud provider that is not implemented yet." } } diff --git a/_locales/sq/messages.json b/_locales/sq/messages.json index 5a1c36461..ab2c7b670 100644 --- a/_locales/sq/messages.json +++ b/_locales/sq/messages.json @@ -1,14 +1,22 @@ { "extName": { - "message": "Autentifikuesi", + "message": "OTPilot Authenticator", "description": "Extension Name." }, "extShortName": { - "message": "Autentifikuesi", + "message": "OTPilot", "description": "Extension Short Name." }, + "host": { + "message": "Faqja e internetit", + "description": "Bound website host used to restrict autofill to a matching page." + }, + "backup_requires_password": { + "message": "Vendosni fillimisht një fjalëkalim për të përdorur backup-in në cloud, që kodet tuaja sekrete të kriptohen përpara se të largohen nga kjo pajisje.", + "description": "Shown in the cloud backup section when no master password is set." + }, "extDesc": { - "message": "Autentifikuesi gjeneron kode autentifikimi me 2 faktorë në shletuesin tuaj.", + "message": "OTPilot Authenticator gjeneron kode me dy faktorë (2FA), me kopje rezervë të enkriptuar në cloud, plotësim automatik të përshtatur me faqen e internetit, dhe këshilla sigurie.", "description": "Extension Description." }, "added": { @@ -23,6 +31,10 @@ "message": "Kodi sekret i llogarisë është i pavlefshëm", "description": "Secret Error." }, + "errorissuer": { + "message": "Lëshuesi nuk mund të përmbajë \"::\"", + "description": "Issuer Error." + }, "add_code": { "message": "Shto llogari", "description": "Add account." @@ -40,13 +52,9 @@ "description": "Import migration data failed." }, "migration_partly_fail": { - "message": "Disa të dhëna të llogarisë nuk janë importuar në mënyrë të sukseshme.", + "message": "Disa të dhëna të llogarisë nuk u importuan me sukses.", "description": "Some migration data is broken." }, - "close": { - "message": "Mbyll", - "description": "Close." - }, "ok": { "message": "Në rregull", "description": "OK." @@ -68,7 +76,7 @@ "description": "Account Name." }, "issuer": { - "message": "Lëshuar nga", + "message": "Lëshuesi", "description": "Issuer." }, "secret": { @@ -99,10 +107,6 @@ "message": "Fjalëkalimi aktual", "description": "Current Passphrase." }, - "new_phrase": { - "message": "Fjalëkalim i ri", - "description": "New Passphrase." - }, "phrase": { "message": "Fjalëkalimi", "description": "Passphrase." @@ -112,37 +116,33 @@ "description": "Confirm Passphrase." }, "confirm_delete": { - "message": "A jeni i sigurtë qe doni të fshini këtë llogari? Ky veprim nuk mund të zhbëhet.", + "message": "A jeni i sigurt që doni ta fshini këtë llogari? Ky veprim nuk mund të zhbëhet.", "description": "Remove entry confirmation" }, "confirm_delete_all": { - "message": "E kuptoj se të gjithë të dhënat e mia do të fshihen dhe do të jenë të parikuperueshme.", + "message": "E kuptoj se të gjitha të dhënat e mia do të fshihen përfundimisht dhe nuk do të mund të rikuperohen.", "description": "Message that user is required to acknowledge before clearing all data." }, "delete_all": { - "message": "Kthe në gjendje fillestare Authenticator-in" + "message": "Kthe OTPilot-in në gjendjen fillestare" }, "delete_all_warning": { - "message": "Kjo do të fshijë të gjithë plotësisht të dhënat tuaja dhe kthejë Authenticator në gjendje fillestare. Ju nuk do të jeni në gjendje të riktheni të dhënat e fshira! Ju duhet të konsideroni ruajtjen e një kopje rezervë përpara rikthimit në gjëndje fillestare të Authenticator-it." + "message": "Kjo do të fshijë plotësisht të gjitha të dhënat tuaja dhe do ta kthejë OTPilot-in në gjendjen fillestare. Nuk do të jeni në gjendje të riktheni asnjë të dhënë të fshirë! Duhet të konsideroni ruajtjen e një kopje rezervë përpara se ta rivendosni OTPilot-in." }, "security_warning": { - "message": "Ky fjalëkalim do të përdoret për të kriptuar llogarinë tënde. Askush nuk mund tju ndihmojë ju nëse ju harroni fjalëkalimin.", + "message": "Ky fjalëkalim do të përdoret për të enkriptuar llogaritë tuaja. Askush nuk mund t'ju ndihmojë nëse harroni fjalëkalimin.", "description": "Passphrase Warning." }, - "update": { - "message": "Përditëso", - "description": "Update." - }, "phrase_incorrect": { - "message": "Ju nuk mund të shtoni një llogari të re derisa të gjitha llogaritë janë të dekriptuara. Ju lutem vendosni fjalkalimin korrekt përpara se të vazhdoni.", + "message": "Ju nuk mund të shtoni një llogari të re derisa të gjitha llogaritë janë të dekriptuara. Ju lutem vendosni fjalëkalimin korrekt përpara se të vazhdoni.", "description": "Passphrase Incorrect." }, "phrase_incorrect_export": { - "message": "Llogaritë që nuk mundën të dekriptohen nuk do të përfshihen në këtë backup.", + "message": "Llogaritë që nuk munden të dekriptohen nuk do të përfshihen në këtë kopje rezervë.", "description": "Skip Unable-decripted Data." }, "phrase_not_match": { - "message": "Fajlëkalimi nuk përputhet.", + "message": "Fjalëkalimi nuk përputhet.", "description": "Passphrase Not Match." }, "encrypted": { @@ -157,10 +157,6 @@ "message": "Reagime", "description": "Feedback." }, - "translate": { - "message": "Përkthe", - "description": "Translate." - }, "source": { "message": "Kodi burimor", "description": "Source Code." @@ -173,24 +169,20 @@ "message": "Sinkronizo Orën me Google", "description": "Sync Clock" }, - "remember_phrase": { - "message": "Kujto fjalëkalimin", - "description": "Remember Passphrase" - }, "clock_too_far_off": { - "message": "Kujdes! Ora juaj lokale është mbrapa, ju lutem rregullojeni përpara se të vazhdoni.", + "message": "Kujdes! Ora juaj lokale është shumë e pasaktë, ju lutem rregullojeni përpara se të vazhdoni.", "description": "Local Time is Too Far Off" }, "remind_backup": { - "message": "A keni backup për llogaritë tuaja? Mos prit derisa të jetë shumë vonë!", + "message": "A keni kopje rezervë për llogaritë tuaja? Mos prisni derisa të jetë shumë vonë!", "description": "Remind Backup" }, "capture_failed": { - "message": "Rregjistrimi i imazhit dështoi, ju lutem ringarkoni faqen dhe provoni përsëri.", + "message": "Regjistrimi i imazhit dështoi, ju lutem ringarkoni faqen dhe provoni përsëri.", "description": "Capture Failed" }, "capture_local_file_failed": { - "message": "A jeni duke provuar skanimin e kodit QR nga një skedar lokal? Në vënd të kësaj përdor importimin e kopjes rezervë të imazhit QR.", + "message": "A jeni duke provuar të skanoni kodin QR nga një skedar lokal? Në vend të kësaj, përdorni importimin e kopjes rezervë të imazhit QR.", "description": "Import QR image backup instead of scan local image" }, "based_on_time": { @@ -210,43 +202,75 @@ "description": "Scale" }, "export_info": { - "message": "Lajmërim: të gjitha kopjet rezervë nuk janë të kriptura. Kërkoni të shtoni një llogari në një app tjetër? Shkoni në krye në anën e djathtë të cdo llogarie dhe shtyp butonin 'hidden'.", + "message": "Lajmërim: të gjitha kopjet rezervë janë të pakriptuara. Doni të shtoni një llogari në një aplikacion tjetër? Kaloni me miun mbi pjesën e sipërme djathtas të çdo llogarie dhe shtypni butonin e fshehur.", "description": "Export menu info text" }, "download_backup": { - "message": "Shkarko skedarin Backup", + "message": "Shkarko skedarin e kopjes rezervë", "description": "Download backup file." }, "import_backup": { - "message": "Importo Backup", + "message": "Importo kopjen rezervë", "description": "Import backup." }, "import_backup_file": { - "message": "Importo skedarin Backup", + "message": "Importo skedarin e kopjes rezervë", "description": "Import backup file." }, "import_backup_qr": { - "message": "Importo Backup të imazhit QR", + "message": "Importo kopjen rezervë nga imazhi QR", "description": "Import qr image backup." }, "import_qr_images": { - "message": "استحضار صور رموز الاستجابة السريع", + "message": "Importo imazhe QR", "description": "Import qr images. Shown as add account method." }, "import_backup_code": { - "message": "Importo Backup të tekstit", + "message": "Importo OTPAuth URI", "description": "Import backup code." }, "import_otp_urls": { - "message": "إستحضار رمز المرور المؤقت", + "message": "Importo OTPAuth URI", "description": "Import OTP URLs. Shown as add account method." }, + "import_drop_file": { + "message": "Lësho një skedar kopje rezervë këtu, ose kliko për të shfletuar", + "description": "File import dropzone title" + }, + "import_file_accepts": { + "message": "Pranon eksportime .json dhe .txt", + "description": "File import dropzone hint" + }, + "import_choose_file": { + "message": "Zgjidh skedarin", + "description": "File import dropzone button" + }, + "import_encrypted_note": { + "message": "Nëse skedari është i enkriptuar, do t'ju kërkohet fjalëkalimi i tij pasi ta zgjidhni.", + "description": "File import note about encrypted backups" + }, + "import_enc_required": { + "message": "Enkriptuar — kërkohet fjalëkalim", + "description": "Badge on a picked encrypted backup file" + }, + "import_pass_placeholder": { + "message": "Vendos fjalëkalimin e skedarit", + "description": "Passphrase input placeholder for encrypted import" + }, + "import_pass_hint": { + "message": "Ky është fjalëkalimi që keni vendosur kur krijuat kopjen rezervë.", + "description": "Hint under the import passphrase field" + }, + "import_decrypt": { + "message": "Dekripto dhe importo", + "description": "Button to decrypt and import an encrypted backup" + }, "import_backup_qr_partly_failed": { - "message": "تم الاستحضار بنجاح، لاكن بعض رموز الاستجابة السريعة لم يتم التعرف عليها.", + "message": "Importimi u krye me sukses, por disa kode QR nuk u njohën.", "description": "Import successful, but some QR image cannot be recognized." }, "import_backup_qr_in_batches": { - "message": "Ju mund të selektoni shumë skedarë për të importuar në sasi kopjet rezervë.", + "message": "Ju mund të zgjidhni disa skedarë për të importuar kopjet rezervë në grupe.", "description": "You can select multiple image files to import backup in batches." }, "show_all_entries": { @@ -254,15 +278,15 @@ "description": "Show all entries." }, "dropbox_risk": { - "message": "Lajmërim: kopjet rezervë nuk janë të kriptuara. Përdorni duke marrë vetë rrezikun.", + "message": "Lajmërim: kopjet rezervë nuk janë të enkriptuara. Përdorimi bëhet me përgjegjësinë tuaj.", "description": "Backup risk warning." }, "import_error_password": { - "message": "Ju duhet të ofroni fjalëkalimin korrekt për importimin e kopjeve rezervë.", + "message": "Ju duhet të vendosni fjalëkalimin e saktë për të importuar kopjet rezervë.", "description": "Error password warning when import backups." }, "local_passphrase_warning": { - "message": "Fjalëkalimi juaj është ruajtur në mënyrë lokale, ju lutem menjëherë ndryshoni atë në menu-ja e sigurisë.", + "message": "Fjalëkalimi juaj është ruajtur në mënyrë lokale, ju lutem ndryshojeni menjëherë në menynë e sigurisë.", "description": "localStorage password warning." }, "remove": { @@ -290,51 +314,135 @@ "description": "Edit" }, "manual_dropbox": { - "message": "Krijo kopje rezervë", + "message": "Kopje rezervë manuale", "description": "Manual backup" }, "use_autofill": { "message": "Përdor Autofill", "description": "Use Autofill" }, - "use_high_contrast": { - "message": "Përdor kontrast të lartë", - "description": "Use High Contrast" - }, "theme": { "message": "Tema", "description": "Theme" }, + "settings_appearance": { + "message": "Pamja", + "description": "Settings section: appearance" + }, + "settings_general": { + "message": "Të përgjithshme", + "description": "Settings section: general" + }, + "edit_accounts": { + "message": "Ndrysho llogaritë", + "description": "Header title while in edit mode" + }, + "show_qr": { + "message": "Shfaq kodin QR", + "description": "Context-menu action to show an account's QR code" + }, + "pin_to_top": { + "message": "Ngjit në krye", + "description": "Context-menu action to pin an account to the top" + }, + "unpin": { + "message": "Hiq nga krye", + "description": "Context-menu action to unpin an account" + }, + "vault_locked": { + "message": "Kasaforta e kyçur", + "description": "Title on the unlock screen" + }, + "unlock": { + "message": "Zhkyç", + "description": "Unlock button" + }, + "qr_transfer_title": { + "message": "Transfero këtë llogari", + "description": "QR sheet caption title" + }, + "qr_transfer_desc": { + "message": "Skano këtë kod me OTPilot në një pajisje tjetër për të transferuar këtë llogari — ajo nuk largohet kurrë nga pajisjet tuaja.", + "description": "QR sheet caption body" + }, + "backup_on_device": { + "message": "Në këtë pajisje", + "description": "Backup section: local" + }, + "backup_cloud_sync": { + "message": "Sinkronizim në Cloud", + "description": "Backup section: cloud" + }, + "backup_connected": { + "message": "I lidhur", + "description": "Cloud provider connected status" + }, + "backup_not_connected": { + "message": "I palidhur", + "description": "Cloud provider not-connected status" + }, + "showing_codes_for": { + "message": "Duke shfaqur kodet për", + "description": "Smart-filter banner prefix, followed by the site domain" + }, + "other_accounts": { + "message": "Llogari të tjera", + "description": "Divider above non-matching accounts in the filter view" + }, + "filter_to_site": { + "message": "Filtro sipas", + "description": "Banner prefix to re-apply the site filter, followed by the domain" + }, + "onboarding_title": { + "message": "Kode që udhëtojnë me ju", + "description": "Onboarding headline" + }, + "onboarding_subtitle": { + "message": "Krijoni kode të sigurta me dy faktorë për çdo llogari — të enkriptuara, offline, dhe gjithmonë vetëm një prekje larg kopjimit.", + "description": "Onboarding subtitle" + }, + "onboarding_feature_offline": { + "message": "Funksionon plotësisht offline — asgjë nuk largohet nga pajisja juaj", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_encrypted": { + "message": "Ruajtje e enkriptuar me kyçje fjalëkalimi opsionale", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_scan": { + "message": "Skano një kod QR ose importo një kopje rezervë ekzistuese", + "description": "Onboarding feature bullet" + }, + "onboarding_get_started": { + "message": "Fillo", + "description": "Onboarding primary button" + }, + "onboarding_import": { + "message": "Kam një kopje rezervë për të importuar", + "description": "Onboarding secondary button" + }, "theme_light": { - "message": "E çelur", + "message": "E çelët", "description": "Light theme" }, "theme_dark": { "message": "E errët", "description": "Dark theme" }, - "theme_simple": { - "message": "بسيط", - "description": "Simple theme" + "theme_auto": { + "message": "Automatik", + "description": "Follow the system light/dark setting" }, "theme_compact": { - "message": "معقد او مركب", + "message": "Kompakte", "description": "Compact theme" }, "theme_high_contrast": { "message": "Kontrast i lartë", "description": "High Contrast theme" }, - "theme_flat": { - "message": "مسطح", - "description": "Flat theme" - }, - "storage_sync_info": { - "message": "Ruaj në mënyrë automatike një kopje rezervë të të dhënave tuaja në shërbimet me palë të 3ta të magazinimit.", - "description": "3rd party backup info" - }, "browser_sync": { - "message": "Sinkronizo Shfletuesin", + "message": "Sinkronizim i shfletuesit", "description": "Storage location" }, "sign_in": { @@ -353,7 +461,7 @@ "description": "Sign out of 3rd party storage services" }, "token_revoked": { - "message": "Është një problem në lidhjen me të dhënat e llogarise suaj $SERVICE$, ju identikohuni përsëri.", + "message": "Pati një problem në lidhjen me llogarinë tuaj në $SERVICE$, ju lutem identifikohuni përsëri.", "description": "Error authenticating to backup service. $SERVICE$ will be replaced with a proper noun (E.g.: 'Google Drive' or 'Dropbox')", "placeholders": { "service": { @@ -362,12 +470,8 @@ } } }, - "otp_unsupported_warn": { - "message": "Ju keni një ose më shumë llogari Steam ose Blizzard. Backup-et jo të kriptuar nuk do të përdorin formatin backup të standardizuar.", - "description": "Warning if using account that is not supported by standard backup format." - }, "otp_backup_inform": { - "message": "Ju mund të importoni backup-et nga disa aplikacione të tjerë.", + "message": "Ju mund të importoni kopje rezervë nga disa aplikacione të tjera.", "description": "Info text on import page" }, "otp_backup_learn": { @@ -384,7 +488,7 @@ "message": "minuta" }, "advanced": { - "message": "E Avancuar" + "message": "E avancuar" }, "period": { "message": "Periudha" @@ -396,7 +500,7 @@ "message": "I pavlefshëm" }, "digits": { - "message": "Numrat" + "message": "Shifrat" }, "algorithm": { "message": "Algoritmi" @@ -405,120 +509,109 @@ "message": "Filtri inteligjent" }, "backup": { - "message": "Krijo kopje rezervë" - }, - "backup_file_info": { - "message": "Krijo një kopje rezervë të të dhënave tuaja në një skedar." + "message": "Kopje rezervë" }, "password_policy_default_hint": { - "message": "كلمة مرورك الخاصة لا تستوفى المتطلبات الخاصة بمنظمتك. الرجاء التواصل مع مديرك للمزيد من المعلومات." + "message": "Fjalëkalimi juaj nuk i plotëson kërkesat e sigurisë të organizatës suaj. Kontaktoni administratorin tuaj për më shumë informacion." }, "advisor": { - "message": "مساعدة" + "message": "Këshilltar" }, "advisor_insight_password_not_set": { - "message": "إنشاء كلمة مرور لحماية بياناتك." + "message": "Vendosni një fjalëkalim për të mbrojtur të dhënat tuaja." }, "advisor_insight_auto_lock_not_set": { - "message": "الرجاء تفعيل الإغلاق الالى لحماية بياناتك." + "message": "Aktivizoni kyçjen automatike për të mbrojtur të dhënat tuaja." }, "advisor_insight_browser_sync_not_enabled": { - "message": "مزامنة المتصفح غير نشط. تفعيل المزامنة يسمح لحسابك بالمزامنه مع المواقع المختلفة." + "message": "Sinkronizimi i shfletuesit është i çaktivizuar. Aktivizimi i tij lejon sinkronizimin e llogarive ndërmjet shfletuesve." }, "advisor_insight_auto_fill_not_enabled": { - "message": "تفعيل الملء التلقائي يسمح بملء رموز التفعيل داخل المتصفحات المختلفة." + "message": "Plotësimi automatik mund të aktivizohet për të plotësuar automatikisht kodet në faqet e internetit." }, "advisor_insight_smart_filter_not_enabled": { - "message": "تفعيل المنقى الذكى يسمح بالدخول بسرعة الى مختلف الحسابات." + "message": "Aktivizimi i filtrit inteligjent lejon qasje të shpejtë në llogaritë tuaja." }, "show_all_insights": { - "message": "إظهار كافة الاقتراحات." + "message": "Shfaq të gjitha këshillat." }, "no_insight_available": { - "message": "لا يوجد اى إقتراحات، كل شئ يبدو جيد!" + "message": "Nuk u gjet asnjë këshillë, gjithçka duket në rregull!" }, "danger": { - "message": "خطر" + "message": "Rrezik" }, "warning": { - "message": "تحزير" + "message": "Paralajmërim" }, "info": { - "message": "معلومات" + "message": "Informacion" }, "dismiss": { - "message": "الغاء" + "message": "Hiq" }, "learn_more": { - "message": "معرفة المزيد" + "message": "Mëso më shumë" }, "enable_context_menu": { - "message": "إضافة الى قائمة السياق النصى" + "message": "Shto në menynë e kontekstit" }, "no_entires": { - "message": "لا يوجد لديك حسابات لإظهاره. انشاء حسابك الاول الان." + "message": "Nuk ka llogari për të shfaqur. Shtoni llogarinë tuaj të parë tani." }, "permissions": { - "message": "الموافقة" + "message": "Lejet" }, "permission_revoke": { - "message": "إبطال" + "message": "Revoko" }, "permission_show_required_permissions": { - "message": "اظهار رموز المرور الغير قابلة للإلغاء" + "message": "Shfaq lejet që nuk mund të revokohen." }, "permission_required": { - "message": "هذا رمز مرور الزامي ولا يمكن ابطاله." + "message": "Ky është një leje e detyrueshme dhe nuk mund të revokohet." }, "permission_active_tab": { - "message": "الرجاء اختيار علامة التبويب الحالية لمسح رمز الاستجابة السريع." + "message": "Qasje te skeda aktuale për të skanuar kodet QR." }, "permission_storage": { - "message": "الرجاء الدخول الى مساحة تخزين المتصفح لتخزين بيانات الحساب." + "message": "Qasje te hapësira ruajtëse e shfletuesit për të ruajtur të dhënat e llogarisë." }, "permission_identity": { - "message": "يسمح بالمرور إلى خدمات تخزين الطرف الثالث." + "message": "Lejon identifikimin në shërbimet e ruajtjes së palëve të treta." }, "permission_alarms": { - "message": "يسمح للإغلاق التلقائي بالعمل." + "message": "Lejon funksionimin e kyçjes automatike." }, "permission_scripting": { - "message": "ادخل النص إلى علامة التبويب الحالية لمسح رموز الإستجابة السريعة والسماح للملء التلقائي بالعمل." + "message": "Injekton skripte në skedën aktuale për të skanuar kodet QR dhe për të mundësuar funksionimin e plotësimit automatik." }, "permission_clipboard_write": { - "message": "يضمن حق الوصول للكتابة فقط إلى الحافظة لنسخ الرموز إلى الحافظة عند النقر فوق الحساب." + "message": "Jep qasje vetëm për shkrim në clipboard, për të kopjuar kodet aty kur klikoni mbi llogarinë." }, "permission_context_menus": { - "message": "يضيف المصادقة إلى قائمة السياق النصى." + "message": "Shton OTPilot në menynë e kontekstit." }, "permission_sync_clock": { - "message": "يسمح بمزامنة الساعة مع جوجل." + "message": "Lejon sinkronizimin e orës me Google." }, "permission_dropbox": { - "message": "يسمح بنسخة احتياطية فى موقع Dropbox." + "message": "Lejon krijimin e kopjeve rezervë në Dropbox." }, "permission_dropbox_cannot_revoke": { - "message": "يجب الغاء تفعيل النسخة الاحتياطية على موقع Dropbox أولاً." - }, - "permission_drive": { - "message": "السماح بالنسخ الاحتياطى على موقع Google Drive." - }, - "permission_drive_cannot_revoke": { - "message": "يجب إلغاء تفعيل النسخ الاحتياطية على موقع Google Drive أولاً." - }, - "permission_onedrive": { - "message": "السماح بالنسخ الاحتياطي على موقع OneDrive." - }, - "permission_onedrive_cannot_revoke": { - "message": "الغاء تفعيل النسخة الاحتياطي على موقع OneDrive أولاً." + "message": "Duhet të çaktivizoni fillimisht kopjen rezervë në Dropbox." }, "permission_unknown_permission": { - "message": "موافقة مجهولة. اذا رأيت هذه الرساله، الرجاء ارسال تقرير اختراق." + "message": "Leje e panjohur. Nëse e shihni këtë mesazh, ju lutem dërgoni një raport gabimi." }, "phrase_wrong": { - "message": "Password incorrect" + "message": "Fjalëkalimi është i pasaktë." }, "activate_auto_filter": { - "message": "Warning: Smart filter loosely matches the domain name to an account. Always verify that you are on the correct website before entering a code!" + "message": "Kujdes: Filtri inteligjent përputh në mënyrë të përafërt emrin e domainit me një llogari. Gjithmonë verifikoni që jeni në faqen e saktë përpara se të vendosni një kod!" + }, + "backup_unavailable": { + "message": "Ende jo i disponueshëm", + "description": "Shown on a cloud provider that is not implemented yet." } } diff --git a/_locales/sr/messages.json b/_locales/sr/messages.json index 9e4ccff64..9930b1c16 100644 --- a/_locales/sr/messages.json +++ b/_locales/sr/messages.json @@ -1,18 +1,26 @@ { "extName": { - "message": "Аутентификатор", + "message": "OTPilot Authenticator", "description": "Extension Name." }, "extShortName": { - "message": "Аутентификатор", + "message": "OTPilot", "description": "Extension Short Name." }, + "host": { + "message": "Веб-сајт", + "description": "Bound website host used to restrict autofill to a matching page." + }, + "backup_requires_password": { + "message": "Прво подесите лозинку да бисте користили резервну копију у облаку, како би ваше тајне биле шифроване пре него што напусте овај уређај.", + "description": "Shown in the cloud backup section when no master password is set." + }, "extDesc": { - "message": "Аутентификатор генерише кодове за приступ у два степена.", + "message": "OTPilot Authenticator генерише кодове за двофакторску аутентификацију (2FA), уз шифровану резервну копију у облаку, аутоматско попуњавање усклађено с веб-сајтом и безбедносне савете.", "description": "Extension Description." }, "added": { - "message": " додато.", + "message": " је додато.", "description": "Added Account." }, "errorqr": { @@ -23,6 +31,10 @@ "message": "Неважећа тајна", "description": "Secret Error." }, + "errorissuer": { + "message": "Издавач не сме да садржи „::“", + "description": "Issuer Error." + }, "add_code": { "message": "Додај налог", "description": "Add account." @@ -36,17 +48,13 @@ "description": "Manual Entry." }, "migration_fail": { - "message": "Увоз није успео. Ако мигрирате податке из Google Authenticatorа, молимо поновно извезите и покушајте опет.", + "message": "Увоз није успео. Ако мигрирате податке из Google Authenticator-а, поново извезите своје податке из Google Authenticator-а и покушајте поново.", "description": "Import migration data failed." }, "migration_partly_fail": { - "message": "Неки подаци о налоги нису успешно увезени.", + "message": "Неки подаци о налозима нису успешно увезени.", "description": "Some migration data is broken." }, - "close": { - "message": "Затвори", - "description": "Close." - }, "ok": { "message": "Ok", "description": "OK." @@ -96,13 +104,9 @@ "description": "Security." }, "current_phrase": { - "message": "Актуелна лозинка", + "message": "Тренутна лозинка", "description": "Current Passphrase." }, - "new_phrase": { - "message": "Нова лозинка", - "description": "New Passphrase." - }, "phrase": { "message": "Лозинка", "description": "Passphrase." @@ -120,25 +124,21 @@ "description": "Message that user is required to acknowledge before clearing all data." }, "delete_all": { - "message": "Ресетуј аутентификатор" + "message": "Ресетуј OTPilot" }, "delete_all_warning": { - "message": "Ово че обрисати све ваше податке и потпуно ресетовати Атентификатор. Нећете моћи вратити обрисане податке. Сачувајте копију података прије ресетовања аутентификатора." + "message": "Ово ће обрисати све ваше податке и потпуно ресетовати OTPilot. Нећете моћи да повратите обрисане податке! Размислите о чувању резервне копије пре ресетовања OTPilot-а." }, "security_warning": { - "message": "Овом ће се лозинком шифрирати ваш налог. Нитко вам неће моћи помоћи ако је заборавите.", + "message": "Ова лозинка ће се користити за шифровање ваших налога. Нико вам неће моћи помоћи ако је заборавите.", "description": "Passphrase Warning." }, - "update": { - "message": "Ажурирање", - "description": "Update." - }, "phrase_incorrect": { - "message": "Не можете да додате нови налог док су сви налози дешифрирани. Молимо унесите точну лозинку пре наставка.", + "message": "Не можете да додате нови налог док су сви налози дешифровани. Молимо унесите тачну лозинку пре наставка.", "description": "Passphrase Incorrect." }, "phrase_incorrect_export": { - "message": "Налози које се није могло дешифровати, неће бити укључени у овај бекап.", + "message": "Налози које није било могуће дешифровати неће бити укључени у ову резервну копију.", "description": "Skip Unable-decripted Data." }, "phrase_not_match": { @@ -157,36 +157,28 @@ "message": "Повратне информације", "description": "Feedback." }, - "translate": { - "message": "Преведи", - "description": "Translate." - }, "source": { "message": "Изворни код", "description": "Source Code." }, "passphrase_info": { - "message": "Унесите лозинку за дешифроваље података.", + "message": "Унесите лозинку за дешифровање података налога.", "description": "Passphrase Info" }, "sync_clock": { - "message": "Синхронизуј часовник са Гуглом", + "message": "Синхронизуј часовник са Google-ом", "description": "Sync Clock" }, - "remember_phrase": { - "message": "Запамти лозинку", - "description": "Remember Passphrase" - }, "clock_too_far_off": { - "message": "Позоир! Ваш локални часовник је превише неусклађен. Молимо подесите га пре наставка.", + "message": "Опрез! Ваш локални часовник је превише неусклађен. Молимо подесите га пре наставка.", "description": "Local Time is Too Far Off" }, "remind_backup": { - "message": "Имате ли копију ваших налога? Немојте чекати да буде касно!", + "message": "Имате ли резервну копију ваших налога? Немојте чекати да буде касно!", "description": "Remind Backup" }, "capture_failed": { - "message": "Хватање није успело, молимо освежите и покушајте поновно.", + "message": "Снимање није успело, молимо освежите страницу и покушајте поново.", "description": "Capture Failed" }, "capture_local_file_failed": { @@ -194,7 +186,7 @@ "description": "Import QR image backup instead of scan local image" }, "based_on_time": { - "message": "Zasnovan на времену", + "message": "Заснован на времену", "description": "Time Based" }, "based_on_counter": { @@ -210,7 +202,7 @@ "description": "Scale" }, "export_info": { - "message": "Упозорење: Све резервне копије су дешифроване. Хоћете да додате налог у дгугу апликацију? Лебдите изнад горњег десног дела било којег налога и притисните дугме Скривено.", + "message": "Упозорење: све резервне копије су нешифроване. Желите да додате налог у другу апликацију? Пређите мишем преко горњег десног дела било ког налога и притисните скривено дугме.", "description": "Export menu info text" }, "download_backup": { @@ -222,7 +214,7 @@ "description": "Import backup." }, "import_backup_file": { - "message": "Увези резервну копију", + "message": "Увези датотеку резервне копије", "description": "Import backup file." }, "import_backup_qr": { @@ -230,19 +222,51 @@ "description": "Import qr image backup." }, "import_qr_images": { - "message": "Import QR Images", + "message": "Увези QR слике", "description": "Import qr images. Shown as add account method." }, "import_backup_code": { - "message": "Увези текстуалну копију", + "message": "Увези OTPAuth URI", "description": "Import backup code." }, "import_otp_urls": { - "message": "Import OTP URLs", + "message": "Увези OTPAuth URI", "description": "Import OTP URLs. Shown as add account method." }, + "import_drop_file": { + "message": "Превуците резервну копију овде или кликните да прегледате", + "description": "File import dropzone title" + }, + "import_file_accepts": { + "message": "Прихвата .json и .txt извозе", + "description": "File import dropzone hint" + }, + "import_choose_file": { + "message": "Изабери датотеку", + "description": "File import dropzone button" + }, + "import_encrypted_note": { + "message": "Ако је датотека шифрована, бићете упитани за лозинку након што је изаберете.", + "description": "File import note about encrypted backups" + }, + "import_enc_required": { + "message": "Шифровано — потребна је лозинка", + "description": "Badge on a picked encrypted backup file" + }, + "import_pass_placeholder": { + "message": "Унесите лозинку датотеке", + "description": "Passphrase input placeholder for encrypted import" + }, + "import_pass_hint": { + "message": "Ово је лозинка коју сте поставили приликом креирања резервне копије.", + "description": "Hint under the import passphrase field" + }, + "import_decrypt": { + "message": "Дешифруј и увези", + "description": "Button to decrypt and import an encrypted backup" + }, "import_backup_qr_partly_failed": { - "message": "Import successful, but some QR codes could not be recognized.", + "message": "Увоз је успешан, али неки QR кодови нису препознати.", "description": "Import successful, but some QR image cannot be recognized." }, "import_backup_qr_in_batches": { @@ -254,15 +278,15 @@ "description": "Show all entries." }, "dropbox_risk": { - "message": "Упозорење: резервне копије нису шифроване. Користите на властиту одговорност.", + "message": "Упозорење: резервне копије нису шифроване. Користите на сопствену одговорност.", "description": "Backup risk warning." }, "import_error_password": { - "message": "За увоз, морате упиати точну лозинку.", + "message": "За увоз резервних копија морате унети тачну лозинку.", "description": "Error password warning when import backups." }, "local_passphrase_warning": { - "message": "Ваше лозинке су спремљене локално. Молимо одмах их промените у менију Безбедност.", + "message": "Ваша лозинка је сачувана локално, молимо одмах је промените у менију Безбедност.", "description": "localStorage password warning." }, "remove": { @@ -270,7 +294,7 @@ "description": "Remove password." }, "download_enc_backup": { - "message": "Преузмите сигурносну копију заштићену лозинком", + "message": "Преузмите резервну копију заштићену лозинком", "description": "Download Encrypted Backup" }, "search": { @@ -290,21 +314,113 @@ "description": "Edit" }, "manual_dropbox": { - "message": "Manual Backup", + "message": "Ручна резервна копија", "description": "Manual backup" }, "use_autofill": { "message": "Користите аутоматско попуњавање", "description": "Use Autofill" }, - "use_high_contrast": { - "message": "Користи високи контраст", - "description": "Use High Contrast" - }, "theme": { "message": "Тема", "description": "Theme" }, + "settings_appearance": { + "message": "Изглед", + "description": "Settings section: appearance" + }, + "settings_general": { + "message": "Опште", + "description": "Settings section: general" + }, + "edit_accounts": { + "message": "Измени налоге", + "description": "Header title while in edit mode" + }, + "show_qr": { + "message": "Прикажи QR код", + "description": "Context-menu action to show an account's QR code" + }, + "pin_to_top": { + "message": "Закачи на врх", + "description": "Context-menu action to pin an account to the top" + }, + "unpin": { + "message": "Откачи", + "description": "Context-menu action to unpin an account" + }, + "vault_locked": { + "message": "Трезор је закључан", + "description": "Title on the unlock screen" + }, + "unlock": { + "message": "Откључај", + "description": "Unlock button" + }, + "qr_transfer_title": { + "message": "Пренеси овај налог", + "description": "QR sheet caption title" + }, + "qr_transfer_desc": { + "message": "Скенирајте овај код помоћу OTPilot-а на другом уређају да бисте пренели овај налог — никада не напушта ваше уређаје.", + "description": "QR sheet caption body" + }, + "backup_on_device": { + "message": "На овом уређају", + "description": "Backup section: local" + }, + "backup_cloud_sync": { + "message": "Синхронизација у облаку", + "description": "Backup section: cloud" + }, + "backup_connected": { + "message": "Повезано", + "description": "Cloud provider connected status" + }, + "backup_not_connected": { + "message": "Није повезано", + "description": "Cloud provider not-connected status" + }, + "showing_codes_for": { + "message": "Приказани кодови за", + "description": "Smart-filter banner prefix, followed by the site domain" + }, + "other_accounts": { + "message": "Остали налози", + "description": "Divider above non-matching accounts in the filter view" + }, + "filter_to_site": { + "message": "Филтрирај по", + "description": "Banner prefix to re-apply the site filter, followed by the domain" + }, + "onboarding_title": { + "message": "Кодови који путују са вама", + "description": "Onboarding headline" + }, + "onboarding_subtitle": { + "message": "Генеришите безбедне кодове за двофакторску аутентификацију за сваки налог — шифровано, офлајн, и увек само један додир од копирања.", + "description": "Onboarding subtitle" + }, + "onboarding_feature_offline": { + "message": "Ради потпуно офлајн — ништа не напушта ваш уређај", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_encrypted": { + "message": "Шифровани трезор са опционом заштитом лозинком", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_scan": { + "message": "Скенирајте QR код или увезите постојећу резервну копију", + "description": "Onboarding feature bullet" + }, + "onboarding_get_started": { + "message": "Започни", + "description": "Onboarding primary button" + }, + "onboarding_import": { + "message": "Имам резервну копију за увоз", + "description": "Onboarding secondary button" + }, "theme_light": { "message": "Светла", "description": "Light theme" @@ -313,26 +429,18 @@ "message": "Тамна", "description": "Dark theme" }, - "theme_simple": { - "message": "Simple", - "description": "Simple theme" + "theme_auto": { + "message": "Аутоматски", + "description": "Follow the system light/dark setting" }, "theme_compact": { - "message": "Compact", + "message": "Компактна", "description": "Compact theme" }, "theme_high_contrast": { "message": "Велики контраст", "description": "High Contrast theme" }, - "theme_flat": { - "message": "Flat", - "description": "Flat theme" - }, - "storage_sync_info": { - "message": "Аутоматски спреми резервну копију на услугу складиштења.", - "description": "3rd party backup info" - }, "browser_sync": { "message": "Синхронизација прегледача", "description": "Storage location" @@ -342,18 +450,18 @@ "description": "Sign in to 3rd party storage services" }, "sign_in_business": { - "message": "Sign in (Business)", + "message": "Пријавите се (пословни налог)", "description": "Sign in to 3rd party storage services" }, "onedrive_business_perms": { - "message": "Why do business accounts require more permissions?" + "message": "Зашто пословни налози захтевају више дозвола?" }, "log_out": { "message": "Одјавите се", "description": "Sign out of 3rd party storage services" }, "token_revoked": { - "message": "Дошло је до грешке код спанјања на ваш $SERVICE$ налог. Молимо пријавите се поново.", + "message": "Дошло је до проблема при повезивању са вашим $SERVICE$ налогом. Молимо пријавите се поново.", "description": "Error authenticating to backup service. $SERVICE$ will be replaced with a proper noun (E.g.: 'Google Drive' or 'Dropbox')", "placeholders": { "service": { @@ -362,12 +470,8 @@ } } }, - "otp_unsupported_warn": { - "message": "Имате један или више Steam или Blizzard налога. Нешифроване сигурносне копије неће користити стандардизовани формат.", - "description": "Warning if using account that is not supported by standard backup format." - }, "otp_backup_inform": { - "message": "Можете да увезете резервне копију из других апликација.", + "message": "Можете да увезете резервне копије из других апликација.", "description": "Info text on import page" }, "otp_backup_learn": { @@ -407,118 +511,107 @@ "backup": { "message": "Резервна копија" }, - "backup_file_info": { - "message": "Спреми податке у резервну копију." - }, "password_policy_default_hint": { - "message": "Your password does not meet your organization's security requirements. Contact your administrator for more information." + "message": "Ваша лозинка не испуњава безбедносне захтеве ваше организације. Обратите се администратору за више информација." }, "advisor": { - "message": "Advisor" + "message": "Саветник" }, "advisor_insight_password_not_set": { - "message": "Set a password to protect your data." + "message": "Подесите лозинку да бисте заштитили своје податке." }, "advisor_insight_auto_lock_not_set": { - "message": "Enable auto-lock to protect your data." + "message": "Омогућите аутоматско закључавање да бисте заштитили своје податке." }, "advisor_insight_browser_sync_not_enabled": { - "message": "Browser sync is disabled. Enabling it allows accounts to be synced across browsers." + "message": "Синхронизација прегледача је онемогућена. Њеним омогућавањем налози се могу синхронизовати између прегледача." }, "advisor_insight_auto_fill_not_enabled": { - "message": "Autofill can be enabled to automatically fill codes into websites." + "message": "Аутоматско попуњавање можете омогућити како би се кодови аутоматски уносили на веб-сајтове." }, "advisor_insight_smart_filter_not_enabled": { - "message": "Enabling smart filter allows for quick access to accounts." + "message": "Омогућавање паметног филтера пружа брз приступ налозима." }, "show_all_insights": { - "message": "Show all insights." + "message": "Прикажи све савете." }, "no_insight_available": { - "message": "No insights found, everything looks good!" + "message": "Нема пронађених савета, све изгледа добро!" }, "danger": { - "message": "Danger" + "message": "Опасност" }, "warning": { - "message": "Warning" + "message": "Упозорење" }, "info": { - "message": "Info" + "message": "Информације" }, "dismiss": { - "message": "Dismiss" + "message": "Одбаци" }, "learn_more": { - "message": "Learn more" + "message": "Сазнајте више" }, "enable_context_menu": { - "message": "Add to context menu" + "message": "Додај у контекстни мени" }, "no_entires": { - "message": "No accounts to display. Add your first account now." + "message": "Нема налога за приказ. Додајте свој први налог одмах." }, "permissions": { - "message": "Permissions" + "message": "Дозволе" }, "permission_revoke": { - "message": "Revoke" + "message": "Опозови" }, "permission_show_required_permissions": { - "message": "Show non-revocable permissions" + "message": "Прикажи дозволе које се не могу опозвати" }, "permission_required": { - "message": "This is a required permission and cannot be revoked." + "message": "Ово је обавезна дозвола и не може се опозвати." }, "permission_active_tab": { - "message": "Access to the current tab to scan QR codes." + "message": "Приступ тренутној картици ради скенирања QR кодова." }, "permission_storage": { - "message": "Access to browser storage to store account data." + "message": "Приступ складишту прегледача ради чувања података о налозима." }, "permission_identity": { - "message": "Allows sign in to 3rd party storage services." + "message": "Омогућава пријаву на услуге складиштења трећих страна." }, "permission_alarms": { - "message": "Allows auto-lock to work." + "message": "Омогућава рад аутоматског закључавања." }, "permission_scripting": { - "message": "Inject scripts into he current tab to scan QR codes and allow auto-fill to work." + "message": "Убацује скрипте у тренутну картицу ради скенирања QR кодова и омогућавања рада аутоматског попуњавања." }, "permission_clipboard_write": { - "message": "Grants write-only access to the clipboard to copy codes to clipboard when you click on the account." + "message": "Даје приступ само за упис у клипборд, ради копирања кода приликом клика на налог." }, "permission_context_menus": { - "message": "Adds Authenticator to context menu." + "message": "Додаје OTPilot у контекстни мени." }, "permission_sync_clock": { - "message": "Allows clock sync with Google." + "message": "Омогућава синхронизацију часовника са Google-ом." }, "permission_dropbox": { - "message": "Allows backup to Dropbox." + "message": "Омогућава резервну копију на Dropbox-у." }, "permission_dropbox_cannot_revoke": { - "message": "You must disable Dropbox backup first." - }, - "permission_drive": { - "message": "Allows backup to Google Drive." - }, - "permission_drive_cannot_revoke": { - "message": "You must disable Google Drive backup first." - }, - "permission_onedrive": { - "message": "Allows backup to OneDrive." - }, - "permission_onedrive_cannot_revoke": { - "message": "You must disable OneDrive backup first." + "message": "Прво морате онемогућити резервну копију на Dropbox-у." }, "permission_unknown_permission": { - "message": "Unknown permission. If see this message, please send a bug report." + "message": "Непозната дозвола. Ако видите ову поруку, молимо пошаљите извештај о грешци." }, "phrase_wrong": { - "message": "Password incorrect" + "message": "Нетачна лозинка" }, "activate_auto_filter": { - "message": "Warning: Smart filter loosely matches the domain name to an account. Always verify that you are on the correct website before entering a code!" + "message": "Упозорење: Паметни филтер приближно упоређује назив домена са налогом. Увек проверите да ли сте на исправном веб-сајту пре него што унесете код!" + }, + "backup_unavailable": { + "message": "Још није доступно", + "description": "Shown on a cloud provider that is not implemented yet." } } diff --git a/_locales/sv/messages.json b/_locales/sv/messages.json index cd6e1c952..404795da5 100644 --- a/_locales/sv/messages.json +++ b/_locales/sv/messages.json @@ -1,14 +1,22 @@ { "extName": { - "message": "Authenticator", + "message": "OTPilot Authenticator", "description": "Extension Name." }, "extShortName": { - "message": "Authenticator", + "message": "OTPilot", "description": "Extension Short Name." }, + "host": { + "message": "Webbplats", + "description": "Bound website host used to restrict autofill to a matching page." + }, + "backup_requires_password": { + "message": "Ställ in ett lösenord först för att använda molnsäkerhetskopiering, så att dina hemligheter krypteras innan de lämnar den här enheten.", + "description": "Shown in the cloud backup section when no master password is set." + }, "extDesc": { - "message": "Authenticator genererar 2-stegs verifieringskoder i din webbläsare.", + "message": "OTPilot Authenticator genererar tvåfaktorskoder (2FA) med krypterad molnsäkerhetskopiering, värdmatchad autofyll och säkerhetsrådgivning.", "description": "Extension Description." }, "added": { @@ -23,6 +31,10 @@ "message": "Ogiltig kontohemlighet", "description": "Secret Error." }, + "errorissuer": { + "message": "Utgivaren får inte innehålla \"::\"", + "description": "Issuer Error." + }, "add_code": { "message": "Lägg till konto", "description": "Add account." @@ -36,17 +48,13 @@ "description": "Manual Entry." }, "migration_fail": { - "message": "Import misslyckades. Om du migrerar från Google Authenticator; exportera dina data från Google Authenticator på nytt och försök igen.", + "message": "Import misslyckades. Om du migrerar data från Google Authenticator, exportera dina data från Google Authenticator på nytt och försök igen.", "description": "Import migration data failed." }, "migration_partly_fail": { "message": "Vissa kontodata importerades inte.", "description": "Some migration data is broken." }, - "close": { - "message": "Stäng", - "description": "Close." - }, "ok": { "message": "Ok", "description": "OK." @@ -99,10 +107,6 @@ "message": "Nuvarande lösenord", "description": "Current Passphrase." }, - "new_phrase": { - "message": "Nytt lösenord", - "description": "New Passphrase." - }, "phrase": { "message": "Lösenord", "description": "Passphrase." @@ -120,19 +124,15 @@ "description": "Message that user is required to acknowledge before clearing all data." }, "delete_all": { - "message": "Återställ autentiserare" + "message": "Återställ OTPilot" }, "delete_all_warning": { - "message": "Det här kommer att radera alla dina data och återställa Authenticator helt. Du kommer inte kunna återskapa några raderade data! Du bör överväga att spara en säkerhetskopia innan du återställer Authenticator." + "message": "Det här kommer att radera alla dina data och återställa OTPilot helt. Du kommer inte kunna återskapa några raderade data! Du bör överväga att spara en säkerhetskopia innan du återställer OTPilot." }, "security_warning": { "message": "Detta lösenord kommer att användas för att kryptera dina konton. Ingen kan hjälpa dig om du glömmer lösenordet.", "description": "Passphrase Warning." }, - "update": { - "message": "Uppdatera", - "description": "Update." - }, "phrase_incorrect": { "message": "Du kan inte lägga till ett nytt konto förrän alla konton har dekrypterats. Ange rätt lösenord innan du fortsätter.", "description": "Passphrase Incorrect." @@ -157,10 +157,6 @@ "message": "Feedback", "description": "Feedback." }, - "translate": { - "message": "Översätt", - "description": "Translate." - }, "source": { "message": "Källkod", "description": "Source Code." @@ -170,23 +166,19 @@ "description": "Passphrase Info" }, "sync_clock": { - "message": "Synka Klockan med Google", + "message": "Synka klockan med Google", "description": "Sync Clock" }, - "remember_phrase": { - "message": "Kom ihåg lösenord", - "description": "Remember Passphrase" - }, "clock_too_far_off": { - "message": "Varning! Din lokala klocka är för långt ifrån, snälla fixa den innan du fortsätter.", + "message": "Varning! Din lokala klocka går för fel, korrigera den innan du fortsätter.", "description": "Local Time is Too Far Off" }, "remind_backup": { - "message": "Har du en säkerhetskopia på dina konton? Vänta inte tills det är för sent!", + "message": "Har du en säkerhetskopia av dina konton? Vänta inte tills det är för sent!", "description": "Remind Backup" }, "capture_failed": { - "message": "Det gick inte att infånga, ladda om sidan och försök igen.", + "message": "Det gick inte att fånga bilden, ladda om sidan och försök igen.", "description": "Capture Failed" }, "capture_local_file_failed": { @@ -194,11 +186,11 @@ "description": "Import QR image backup instead of scan local image" }, "based_on_time": { - "message": "Tidsbaserade", + "message": "Tidsbaserad", "description": "Time Based" }, "based_on_counter": { - "message": "Räknarebaserade", + "message": "Räknarbaserad", "description": "Counter Based" }, "resize_popup_page": { @@ -210,11 +202,11 @@ "description": "Scale" }, "export_info": { - "message": "Varning: alla säkerhetskopieringar är okrypterade. Vill du lägga till ett konto till en annan app? Håll över den högra högra delen av något konto och tryck på den dolda knappen.", + "message": "Varning: alla säkerhetskopieringar är okrypterade. Vill du lägga till ett konto till en annan app? Håll muspekaren över den övre högra delen av något konto och tryck på den dolda knappen.", "description": "Export menu info text" }, "download_backup": { - "message": "Ladda ner säkerhetskopieringsfilen", + "message": "Ladda ner säkerhetskopieringsfil", "description": "Download backup file." }, "import_backup": { @@ -222,7 +214,7 @@ "description": "Import backup." }, "import_backup_file": { - "message": "Importera säkerhetskopieringsfilen", + "message": "Importera säkerhetskopieringsfil", "description": "Import backup file." }, "import_backup_qr": { @@ -234,15 +226,47 @@ "description": "Import qr images. Shown as add account method." }, "import_backup_code": { - "message": "Importera okrypterad säkerhetskopia", + "message": "Importera OTPAuth-URI", "description": "Import backup code." }, "import_otp_urls": { - "message": "Importera OTP-URL:er", + "message": "Importera OTPAuth-URI", "description": "Import OTP URLs. Shown as add account method." }, + "import_drop_file": { + "message": "Släpp en säkerhetskopieringsfil här, eller klicka för att bläddra", + "description": "File import dropzone title" + }, + "import_file_accepts": { + "message": "Stöder exportfiler i .json- och .txt-format", + "description": "File import dropzone hint" + }, + "import_choose_file": { + "message": "Välj fil", + "description": "File import dropzone button" + }, + "import_encrypted_note": { + "message": "Om filen är krypterad blir du ombedd att ange dess lösenord efter att du valt den.", + "description": "File import note about encrypted backups" + }, + "import_enc_required": { + "message": "Krypterad — lösenord krävs", + "description": "Badge on a picked encrypted backup file" + }, + "import_pass_placeholder": { + "message": "Ange filens lösenord", + "description": "Passphrase input placeholder for encrypted import" + }, + "import_pass_hint": { + "message": "Det här är lösenordet du angav när du skapade säkerhetskopian.", + "description": "Hint under the import passphrase field" + }, + "import_decrypt": { + "message": "Dekryptera och importera", + "description": "Button to decrypt and import an encrypted backup" + }, "import_backup_qr_partly_failed": { - "message": "Import lyckades men vissa QR-koder kunde inte kännas igen.", + "message": "Importen lyckades, men vissa QR-koder kunde inte kännas igen.", "description": "Import successful, but some QR image cannot be recognized." }, "import_backup_qr_in_batches": { @@ -270,7 +294,7 @@ "description": "Remove password." }, "download_enc_backup": { - "message": "Ladda ner lösenordsskyddad säkerhetskopiering", + "message": "Ladda ner lösenordsskyddad säkerhetskopia", "description": "Download Encrypted Backup" }, "search": { @@ -297,14 +321,106 @@ "message": "Använd autofyll", "description": "Use Autofill" }, - "use_high_contrast": { - "message": "Använd hög kontrast", - "description": "Use High Contrast" - }, "theme": { "message": "Tema", "description": "Theme" }, + "settings_appearance": { + "message": "Utseende", + "description": "Settings section: appearance" + }, + "settings_general": { + "message": "Allmänt", + "description": "Settings section: general" + }, + "edit_accounts": { + "message": "Redigera konton", + "description": "Header title while in edit mode" + }, + "show_qr": { + "message": "Visa QR-kod", + "description": "Context-menu action to show an account's QR code" + }, + "pin_to_top": { + "message": "Fäst överst", + "description": "Context-menu action to pin an account to the top" + }, + "unpin": { + "message": "Lossa", + "description": "Context-menu action to unpin an account" + }, + "vault_locked": { + "message": "Valvet är låst", + "description": "Title on the unlock screen" + }, + "unlock": { + "message": "Lås upp", + "description": "Unlock button" + }, + "qr_transfer_title": { + "message": "Överför detta konto", + "description": "QR sheet caption title" + }, + "qr_transfer_desc": { + "message": "Skanna denna kod med OTPilot på en annan enhet för att flytta över detta konto — det lämnar aldrig dina enheter.", + "description": "QR sheet caption body" + }, + "backup_on_device": { + "message": "På denna enhet", + "description": "Backup section: local" + }, + "backup_cloud_sync": { + "message": "Molnsynkronisering", + "description": "Backup section: cloud" + }, + "backup_connected": { + "message": "Ansluten", + "description": "Cloud provider connected status" + }, + "backup_not_connected": { + "message": "Inte ansluten", + "description": "Cloud provider not-connected status" + }, + "showing_codes_for": { + "message": "Visar koder för", + "description": "Smart-filter banner prefix, followed by the site domain" + }, + "other_accounts": { + "message": "Övriga konton", + "description": "Divider above non-matching accounts in the filter view" + }, + "filter_to_site": { + "message": "Filtrera till", + "description": "Banner prefix to re-apply the site filter, followed by the domain" + }, + "onboarding_title": { + "message": "Koder som följer med dig", + "description": "Onboarding headline" + }, + "onboarding_subtitle": { + "message": "Generera säkra tvåfaktorskoder för alla dina konton — krypterat, offline och alltid bara ett tryck från att kopieras.", + "description": "Onboarding subtitle" + }, + "onboarding_feature_offline": { + "message": "Fungerar helt offline — inget lämnar din enhet", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_encrypted": { + "message": "Krypterat valv med valfritt lösenordsskydd", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_scan": { + "message": "Skanna en QR-kod eller importera en befintlig säkerhetskopia", + "description": "Onboarding feature bullet" + }, + "onboarding_get_started": { + "message": "Kom igång", + "description": "Onboarding primary button" + }, + "onboarding_import": { + "message": "Jag har en säkerhetskopia att importera", + "description": "Onboarding secondary button" + }, "theme_light": { "message": "Ljus", "description": "Light theme" @@ -313,9 +429,9 @@ "message": "Mörk", "description": "Dark theme" }, - "theme_simple": { - "message": "Enkel", - "description": "Simple theme" + "theme_auto": { + "message": "Auto", + "description": "Follow the system light/dark setting" }, "theme_compact": { "message": "Kompakt", @@ -325,14 +441,6 @@ "message": "Hög kontrast", "description": "High Contrast theme" }, - "theme_flat": { - "message": "Flat", - "description": "Flat theme" - }, - "storage_sync_info": { - "message": "Säkerhetskopiera dina data automatiskt till lagringstjänster från tredje part.", - "description": "3rd party backup info" - }, "browser_sync": { "message": "Webbläsarsynkronisering", "description": "Storage location" @@ -362,10 +470,6 @@ } } }, - "otp_unsupported_warn": { - "message": "Du har ett eller flera konton hos Steam eller Blizzard. Okrypterade säkerhetskopior kommer inte att använda ett standardiserat format för säkerhetskopior.", - "description": "Warning if using account that is not supported by standard backup format." - }, "otp_backup_inform": { "message": "Du kan importera säkerhetskopior från vissa andra applikationer.", "description": "Info text on import page" @@ -405,10 +509,7 @@ "message": "Smart filter" }, "backup": { - "message": "Säkerhetskopiera" - }, - "backup_file_info": { - "message": "Säkerhetskopiera dina data till en fil." + "message": "Säkerhetskopiering" }, "password_policy_default_hint": { "message": "Ditt lösenord uppfyller inte din organisations säkerhetskrav. Kontakta din administratör för mer information." @@ -420,7 +521,7 @@ "message": "Ställ in ett lösenord för att skydda din data." }, "advisor_insight_auto_lock_not_set": { - "message": "Aktivera automatisk låsning för att skydda dina data." + "message": "Aktivera automatisk låsning för att skydda din data." }, "advisor_insight_browser_sync_not_enabled": { "message": "Webbläsarsynkronisering är inaktiverad. Vid aktivering kan konton synkas mellan webbläsare." @@ -453,7 +554,7 @@ "message": "Lär dig mer" }, "enable_context_menu": { - "message": "Lägg till i snabbvalsmeny" + "message": "Lägg till i snabbmeny" }, "no_entires": { "message": "Inga konton att visa. Lägg till ditt första konto nu." @@ -477,19 +578,19 @@ "message": "Åtkomst till webbläsarlagring för att lagra kontodata." }, "permission_identity": { - "message": "Tillåt inloggning till lagringstjänster från tredje part." + "message": "Tillåter inloggning till lagringstjänster från tredje part." }, "permission_alarms": { "message": "Gör att automatisk låsning fungerar." }, "permission_scripting": { - "message": "Injicerar skript i nuvarande flik för att skanna QR-koder och göra att autofyll fungerar." + "message": "Injicerar skript i den nuvarande fliken för att skanna QR-koder och göra att autofyll fungerar." }, "permission_clipboard_write": { "message": "Ger enbart skrivbehörighet till urklipp för att kopiera koder till urklipp när du klickar på kontot." }, "permission_context_menus": { - "message": "Lägger till Authenticator till snabbvalsmenyn." + "message": "Lägger till OTPilot i snabbmenyn." }, "permission_sync_clock": { "message": "Tillåter klocksynkronisering med Google." @@ -500,18 +601,6 @@ "permission_dropbox_cannot_revoke": { "message": "Du måste inaktivera Dropbox-säkerhetskopiering först." }, - "permission_drive": { - "message": "Tillåter säkerhetskopiering till Google Drive." - }, - "permission_drive_cannot_revoke": { - "message": "Du måste inaktivera Google Drive-säkerhetskopiering först." - }, - "permission_onedrive": { - "message": "Tillåter säkerhetskopiering till OneDrive." - }, - "permission_onedrive_cannot_revoke": { - "message": "Du måste inaktivera OneDrive-säkerhetskopiering först." - }, "permission_unknown_permission": { "message": "Okänd behörighet. Om du ser detta meddelande, skicka en felrapport." }, @@ -519,6 +608,10 @@ "message": "Lösenord felaktigt" }, "activate_auto_filter": { - "message": "Warning: Smart filter loosely matches the domain name to an account. Always verify that you are on the correct website before entering a code!" + "message": "Varning: Smart filter matchar domännamnet löst mot ett konto. Kontrollera alltid att du är på rätt webbplats innan du anger en kod!" + }, + "backup_unavailable": { + "message": "Inte tillgänglig än", + "description": "Shown on a cloud provider that is not implemented yet." } } diff --git a/_locales/th/messages.json b/_locales/th/messages.json index 287a34015..17a1f445b 100644 --- a/_locales/th/messages.json +++ b/_locales/th/messages.json @@ -1,14 +1,22 @@ { "extName": { - "message": "Authentication", + "message": "OTPilot Authenticator", "description": "Extension Name." }, "extShortName": { - "message": "Authentication", + "message": "OTPilot", "description": "Extension Short Name." }, + "host": { + "message": "เว็บไซต์", + "description": "Bound website host used to restrict autofill to a matching page." + }, + "backup_requires_password": { + "message": "ตั้งรหัสผ่านก่อนจึงจะใช้การสำรองข้อมูลบนคลาวด์ได้ เพื่อให้ข้อมูลลับของคุณถูกเข้ารหัสก่อนออกจากอุปกรณ์นี้", + "description": "Shown in the cloud backup section when no master password is set." + }, "extDesc": { - "message": "Authenticator สำหรับสร้างรหัสยืนยัน 2 ชั้นบนเบราว์เซอร์", + "message": "OTPilot Authenticator สร้างรหัสยืนยันตัวตนแบบสองขั้นตอน (2FA) พร้อมการสำรองข้อมูลบนคลาวด์แบบเข้ารหัส การป้อนรหัสอัตโนมัติที่ตรงกับเว็บไซต์ และคำแนะนำด้านความปลอดภัย", "description": "Extension Description." }, "added": { @@ -23,6 +31,10 @@ "message": "รหัสลับของบัญชีไม่ถูกต้อง", "description": "Secret Error." }, + "errorissuer": { + "message": "ผู้ออกต้องไม่มี \"::\"", + "description": "Issuer Error." + }, "add_code": { "message": "เพิ่มบัญชี", "description": "Add account." @@ -40,13 +52,9 @@ "description": "Import migration data failed." }, "migration_partly_fail": { - "message": "บ้างบัญชีไม่สามารถถูกนำเข้าให้สำเร็จได้", + "message": "ข้อมูลบัญชีบางส่วนไม่สามารถนำเข้าได้สำเร็จ", "description": "Some migration data is broken." }, - "close": { - "message": "ปิด", - "description": "Close." - }, "ok": { "message": "ตกลง", "description": "OK." @@ -99,10 +107,6 @@ "message": "รหัสผ่านปัจจุบัน", "description": "Current Passphrase." }, - "new_phrase": { - "message": "รหัสผ่านใหม่", - "description": "New Passphrase." - }, "phrase": { "message": "รหัสผ่าน", "description": "Passphrase." @@ -112,29 +116,25 @@ "description": "Confirm Passphrase." }, "confirm_delete": { - "message": "คุณแน่ใจต้องการลบบัญชีนี้ ? การกระทำดังกล่าวไม่สามารถกู้คืนกลับมาได้", + "message": "คุณแน่ใจหรือไม่ว่าต้องการลบบัญชีนี้? การกระทำนี้ไม่สามารถย้อนกลับได้", "description": "Remove entry confirmation" }, "confirm_delete_all": { - "message": "ฉันเข้าใจว่าข้อมูลทั้งหมดของฉันจะถูกลบเเละไม่สามารถกู้คืนได้", + "message": "ฉันเข้าใจว่าข้อมูลทั้งหมดของฉันจะถูกลบและไม่สามารถกู้คืนได้", "description": "Message that user is required to acknowledge before clearing all data." }, "delete_all": { - "message": "ล้างการตั้งค่าเเอพการตรวจสอบสิทธิ์" + "message": "รีเซ็ต OTPilot" }, "delete_all_warning": { - "message": "นี้คือการลบข้อมูลทั้งหมดของคุณอย่างถาวร การดำเนินการต่อทั้งหมดจะทำให้คุณสูญเสียข้อมูลเเละไม่สามารถกู้กลับคืนได้ กรุณาระมัดระวังเเละสำรองข้อมูลก่อนการดำเนินการต่อ" + "message": "การดำเนินการนี้จะลบข้อมูลทั้งหมดของคุณและรีเซ็ต OTPilot อย่างสมบูรณ์ คุณจะไม่สามารถกู้คืนข้อมูลที่ถูกลบได้เลย ควรพิจารณาสำรองข้อมูลก่อนรีเซ็ต OTPilot" }, "security_warning": { - "message": "รหัสผ่านนี้จะถูกใช้เพื่อเข้ารหัสบัญชีของคุณ ไม่มีใครสามารถช่วยคุณได้หากคุณลืมรหัสผ่าน.", + "message": "รหัสผ่านนี้จะถูกใช้เพื่อเข้ารหัสบัญชีของคุณ ไม่มีใครสามารถช่วยคุณได้หากคุณลืมรหัสผ่าน", "description": "Passphrase Warning." }, - "update": { - "message": "อัพ​เด​ต", - "description": "Update." - }, "phrase_incorrect": { - "message": "คุณไม่สามารถเพิ่มบัญชีใหม่หรือส่งออกข้อมูลจนกว่าบัญชีทั้งหมดจะถูกถอดรหัส กรุณาใส่รหัสผ่านที่ถูกต้องก่อนดำเนินการต่อ", + "message": "คุณไม่สามารถเพิ่มบัญชีใหม่ได้จนกว่าบัญชีทั้งหมดจะถูกถอดรหัส กรุณาใส่รหัสผ่านที่ถูกต้องก่อนดำเนินการต่อ", "description": "Passphrase Incorrect." }, "phrase_incorrect_export": { @@ -154,15 +154,11 @@ "description": "Copied." }, "feedback": { - "message": "เสียงตอบรับ", + "message": "ข้อเสนอแนะ", "description": "Feedback." }, - "translate": { - "message": "แปลภาษา", - "description": "Translate." - }, "source": { - "message": "แหล่งที่มา", + "message": "ซอร์สโค้ด", "description": "Source Code." }, "passphrase_info": { @@ -173,16 +169,12 @@ "message": "ซิงค์เวลาผ่าน Google", "description": "Sync Clock" }, - "remember_phrase": { - "message": "จดจำรหัสผ่าน", - "description": "Remember Passphrase" - }, "clock_too_far_off": { "message": "ระวัง! เวลาของคุณไม่ถูกต้อง โปรดแก้ไขก่อนดำเนินการขั้นต่อไป", "description": "Local Time is Too Far Off" }, "remind_backup": { - "message": "คุณมีข้อมูลสำรองสำหรับบัญชีของคุณหรือไม่ ? โปรดอย่ารอจนสายเกินไป", + "message": "คุณมีข้อมูลสำรองสำหรับบัญชีของคุณหรือไม่? โปรดอย่ารอจนสายเกินไป", "description": "Remind Backup" }, "capture_failed": { @@ -190,15 +182,15 @@ "description": "Capture Failed" }, "capture_local_file_failed": { - "message": "คุณกำลังพยายามสแกนคิวอาร์โค้ดจากไฟล์ในเครื่องหรือไม่? ใช้นำเข้ารูปคิวอาร์โค้ดที่สำรองไว้แทน", + "message": "คุณกำลังพยายามสแกน QR code จากไฟล์ในเครื่องหรือไม่? ใช้การนำเข้ารูป QR code ที่สำรองไว้แทน", "description": "Import QR image backup instead of scan local image" }, "based_on_time": { - "message": "Time Based", + "message": "ตามเวลา", "description": "Time Based" }, "based_on_counter": { - "message": "Counter Based", + "message": "ตามตัวนับ", "description": "Counter Based" }, "resize_popup_page": { @@ -226,7 +218,7 @@ "description": "Import backup file." }, "import_backup_qr": { - "message": "นำเข้ารูปQR การสำรองข้อมูล", + "message": "นำเข้ารูป QR การสำรองข้อมูล", "description": "Import qr image backup." }, "import_qr_images": { @@ -241,8 +233,40 @@ "message": "นำเข้า URL OTP", "description": "Import OTP URLs. Shown as add account method." }, + "import_drop_file": { + "message": "ลากไฟล์สำรองข้อมูลมาวาง หรือคลิกเพื่อเรียกดู", + "description": "File import dropzone title" + }, + "import_file_accepts": { + "message": "รองรับไฟล์ส่งออกนามสกุล .json และ .txt", + "description": "File import dropzone hint" + }, + "import_choose_file": { + "message": "เลือกไฟล์", + "description": "File import dropzone button" + }, + "import_encrypted_note": { + "message": "หากไฟล์ถูกเข้ารหัส ระบบจะขอรหัสผ่านของไฟล์หลังจากที่คุณเลือกไฟล์แล้ว", + "description": "File import note about encrypted backups" + }, + "import_enc_required": { + "message": "เข้ารหัสแล้ว — ต้องใช้รหัสผ่าน", + "description": "Badge on a picked encrypted backup file" + }, + "import_pass_placeholder": { + "message": "ป้อนรหัสผ่านของไฟล์", + "description": "Passphrase input placeholder for encrypted import" + }, + "import_pass_hint": { + "message": "นี่คือรหัสผ่านที่คุณตั้งไว้ตอนสร้างข้อมูลสำรอง", + "description": "Hint under the import passphrase field" + }, + "import_decrypt": { + "message": "ถอดรหัสและนำเข้า", + "description": "Button to decrypt and import an encrypted backup" + }, "import_backup_qr_partly_failed": { - "message": "นำเข้าเสร็จสิ้นเเล้ว เเต่ คิวอาร์โค้ด บางรูปไม่สามารอ่านค่าได้", + "message": "นำเข้าสำเร็จแล้ว แต่ QR code บางรูปไม่สามารถอ่านค่าได้", "description": "Import successful, but some QR image cannot be recognized." }, "import_backup_qr_in_batches": { @@ -297,14 +321,106 @@ "message": "ใช้งานการป้อนอัตโนมัติ", "description": "Use Autofill" }, - "use_high_contrast": { - "message": "ใช้งานความคมชัดสูง", - "description": "Use High Contrast" - }, "theme": { "message": "ธีม", "description": "Theme" }, + "settings_appearance": { + "message": "รูปลักษณ์", + "description": "Settings section: appearance" + }, + "settings_general": { + "message": "ทั่วไป", + "description": "Settings section: general" + }, + "edit_accounts": { + "message": "แก้ไขบัญชี", + "description": "Header title while in edit mode" + }, + "show_qr": { + "message": "แสดง QR Code", + "description": "Context-menu action to show an account's QR code" + }, + "pin_to_top": { + "message": "ปักหมุดไว้บนสุด", + "description": "Context-menu action to pin an account to the top" + }, + "unpin": { + "message": "เลิกปักหมุด", + "description": "Context-menu action to unpin an account" + }, + "vault_locked": { + "message": "ข้อมูลถูกล็อก", + "description": "Title on the unlock screen" + }, + "unlock": { + "message": "ปลดล็อก", + "description": "Unlock button" + }, + "qr_transfer_title": { + "message": "โอนย้ายบัญชีนี้", + "description": "QR sheet caption title" + }, + "qr_transfer_desc": { + "message": "สแกนโค้ดนี้ด้วย OTPilot บนอุปกรณ์อื่นเพื่อโอนย้ายบัญชีนี้ไป — ข้อมูลจะไม่ออกจากอุปกรณ์ของคุณเลย", + "description": "QR sheet caption body" + }, + "backup_on_device": { + "message": "บนอุปกรณ์นี้", + "description": "Backup section: local" + }, + "backup_cloud_sync": { + "message": "ซิงค์บนคลาวด์", + "description": "Backup section: cloud" + }, + "backup_connected": { + "message": "เชื่อมต่อแล้ว", + "description": "Cloud provider connected status" + }, + "backup_not_connected": { + "message": "ยังไม่เชื่อมต่อ", + "description": "Cloud provider not-connected status" + }, + "showing_codes_for": { + "message": "กำลังแสดงรหัสสำหรับ", + "description": "Smart-filter banner prefix, followed by the site domain" + }, + "other_accounts": { + "message": "บัญชีอื่น ๆ", + "description": "Divider above non-matching accounts in the filter view" + }, + "filter_to_site": { + "message": "กรองเฉพาะ", + "description": "Banner prefix to re-apply the site filter, followed by the domain" + }, + "onboarding_title": { + "message": "รหัสยืนยันที่ติดตัวคุณไปทุกที่", + "description": "Onboarding headline" + }, + "onboarding_subtitle": { + "message": "สร้างรหัสยืนยัน 2 ชั้นที่ปลอดภัยให้ทุกบัญชี — เข้ารหัส ใช้งานออฟไลน์ และคัดลอกได้เพียงแตะเดียวเสมอ", + "description": "Onboarding subtitle" + }, + "onboarding_feature_offline": { + "message": "ทำงานแบบออฟไลน์เต็มรูปแบบ — ไม่มีข้อมูลออกจากอุปกรณ์ของคุณ", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_encrypted": { + "message": "ข้อมูลถูกเข้ารหัส พร้อมล็อกด้วยรหัสผ่านได้ตามต้องการ", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_scan": { + "message": "สแกน QR Code หรือนำเข้าข้อมูลสำรองที่มีอยู่", + "description": "Onboarding feature bullet" + }, + "onboarding_get_started": { + "message": "เริ่มต้นใช้งาน", + "description": "Onboarding primary button" + }, + "onboarding_import": { + "message": "ฉันมีข้อมูลสำรองที่ต้องการนำเข้า", + "description": "Onboarding secondary button" + }, "theme_light": { "message": "สว่าง", "description": "Light theme" @@ -313,9 +429,9 @@ "message": "มืด", "description": "Dark theme" }, - "theme_simple": { - "message": "เรียบง่าย", - "description": "Simple theme" + "theme_auto": { + "message": "อัตโนมัติ (ตามระบบ)", + "description": "Follow the system light/dark setting" }, "theme_compact": { "message": "กะทัดรัด", @@ -325,14 +441,6 @@ "message": "ความคมชัดสูง", "description": "High Contrast theme" }, - "theme_flat": { - "message": "แบน", - "description": "Flat theme" - }, - "storage_sync_info": { - "message": "สำรองข้อมูลของคุณโดยอัตโนมัติ ไปยังผู้บริการอื่นๆ", - "description": "3rd party backup info" - }, "browser_sync": { "message": "ซิงค์กับเบราว์เซอร์", "description": "Storage location" @@ -342,7 +450,7 @@ "description": "Sign in to 3rd party storage services" }, "sign_in_business": { - "message": "ลงทะเบียน (ธุรกิจ)", + "message": "ลงชื่อเข้าใช้ (ธุรกิจ)", "description": "Sign in to 3rd party storage services" }, "onedrive_business_perms": { @@ -362,10 +470,6 @@ } } }, - "otp_unsupported_warn": { - "message": "คุณมีบัญชี Steam หรือ Blizzard อย่างน้อยหนึ่งบัญชี ? การสำรองข้อมูลที่ไม่ได้เข้ารหัสจะไม่ใช้รูปแบบการสำรองข้อมูลมาตรฐาน", - "description": "Warning if using account that is not supported by standard backup format." - }, "otp_backup_inform": { "message": "คุณสามารถนำเข้าข้อมูลสำรองจากแอปพลิเคชั่นอื่น ๆ", "description": "Info text on import page" @@ -407,9 +511,6 @@ "backup": { "message": "สำรองข้อมูล" }, - "backup_file_info": { - "message": "สำรองข้อมูลของคุณเป็นไฟล์" - }, "password_policy_default_hint": { "message": "รหัสผ่านของคุณไม่ตรงตามความปลอดภัยขององค์กรของคุณที่กำหนดไว้ สำหรับข้อมูลเพิ่มเติมโปรดติดต่อผู้ดูแลระบบของคุณ" }, @@ -420,13 +521,13 @@ "message": "ตั้งรหัสผ่านเพื่อป้องกันข้อมูลของคุณ." }, "advisor_insight_auto_lock_not_set": { - "message": "เปิดใช้งาน การล็อกอัตโนมัติ เพื่อป้องกันข้อมูลของคุณ" + "message": "เปิดใช้งานการล็อกอัตโนมัติเพื่อป้องกันข้อมูลของคุณ" }, "advisor_insight_browser_sync_not_enabled": { - "message": "Browser sync is disabled. Enabling it allows accounts to be synced across browsers." + "message": "การซิงค์เบราว์เซอร์ถูกปิดใช้งานอยู่ การเปิดใช้งานจะช่วยให้บัญชีของคุณซิงค์ข้ามเบราว์เซอร์ได้" }, "advisor_insight_auto_fill_not_enabled": { - "message": "Autofill can be enabled to automatically fill codes into websites." + "message": "เปิดใช้งานการป้อนอัตโนมัติเพื่อกรอกรหัสลงในเว็บไซต์ให้คุณโดยอัตโนมัติ" }, "advisor_insight_smart_filter_not_enabled": { "message": "เปิดใช้งานตัวกรองอัจฉริยะช่วยให้เข้าถึงบัญชีได้อย่างรวดเร็ว." @@ -447,13 +548,13 @@ "message": "ข้อมูล" }, "dismiss": { - "message": "ยกเลิก" + "message": "ปิด" }, "learn_more": { "message": "เรียนรู้เพิ่มเติม" }, "enable_context_menu": { - "message": "เพิ่มไปยังเมนู" + "message": "เพิ่มไปยังเมนูบริบท" }, "no_entires": { "message": "ยังไม่มีบัญชีผู้ใช้ เพิ่มบัญชีผู้ใช้ของคุณเลยตอนนี้" @@ -480,16 +581,16 @@ "message": "อนุญาตให้ลงชื่อเข้าใช้บริการพื้นที่เก็บข้อมูลของบุคคลที่สาม" }, "permission_alarms": { - "message": "Allows auto-lock to work." + "message": "อนุญาตให้ระบบล็อกอัตโนมัติทำงานได้" }, "permission_scripting": { - "message": "Inject scripts into he current tab to scan QR codes and allow auto-fill to work." + "message": "แทรกสคริปต์เข้าไปในแท็บปัจจุบันเพื่อสแกน QR code และให้การป้อนอัตโนมัติทำงานได้" }, "permission_clipboard_write": { "message": "ให้สิทธิ์การเข้าถึงแบบเขียนอย่างเดียวในคลิปบอร์ดเพื่อคัดลอกรหัสไปยังคลิปบอร์ดเมื่อคุณคลิกที่บัญชี" }, "permission_context_menus": { - "message": "เพิ่ม Authenticator ให้กับเมนูบริบท" + "message": "เพิ่ม OTPilot ให้กับเมนูบริบท" }, "permission_sync_clock": { "message": "อนุญาตให้ซิงค์นาฬิกากับ Google" @@ -500,25 +601,17 @@ "permission_dropbox_cannot_revoke": { "message": "คุณต้องปิดใช้งานการสำรองข้อมูล Dropbox ก่อน" }, - "permission_drive": { - "message": "อนุญาตให้สำรองข้อมูลไปยัง Google Drive" - }, - "permission_drive_cannot_revoke": { - "message": "คุณต้องปิดใช้งานการสำรองข้อมูล Google ไดรฟ์ก่อน" - }, - "permission_onedrive": { - "message": "อนุญาตให้สำรองข้อมูลไปยัง OneDrive" - }, - "permission_onedrive_cannot_revoke": { - "message": "คุณต้องปิดใช้งานการสำรองข้อมูล OneDrive ก่อน" - }, "permission_unknown_permission": { "message": "ไม่ทราบการอนุญาต หากเห็นข้อความนี้ โปรดส่งรายงานข้อบกพร่อง" }, "phrase_wrong": { - "message": "Password incorrect" + "message": "รหัสผ่านไม่ถูกต้อง" }, "activate_auto_filter": { - "message": "Warning: Smart filter loosely matches the domain name to an account. Always verify that you are on the correct website before entering a code!" + "message": "คำเตือน: ตัวกรองอัจฉริยะจะจับคู่ชื่อโดเมนกับบัญชีแบบคร่าว ๆ เท่านั้น โปรดตรวจสอบให้แน่ใจเสมอว่าคุณอยู่บนเว็บไซต์ที่ถูกต้องก่อนป้อนรหัส!" + }, + "backup_unavailable": { + "message": "ยังไม่รองรับ", + "description": "Shown on a cloud provider that is not implemented yet." } } diff --git a/_locales/tr/messages.json b/_locales/tr/messages.json index 2735c5238..bb28721cb 100644 --- a/_locales/tr/messages.json +++ b/_locales/tr/messages.json @@ -1,14 +1,22 @@ { "extName": { - "message": "Kimlik Doğrulayıcı", + "message": "OTPilot Authenticator", "description": "Extension Name." }, "extShortName": { - "message": "Kimlik Doğrulayıcı", + "message": "OTPilot", "description": "Extension Short Name." }, + "host": { + "message": "Web sitesi", + "description": "Bound website host used to restrict autofill to a matching page." + }, + "backup_requires_password": { + "message": "Bulut yedeklemeyi kullanmak için önce bir parola belirleyin; böylece gizli anahtarlarınız bu cihazdan ayrılmadan önce şifrelenir.", + "description": "Shown in the cloud backup section when no master password is set." + }, "extDesc": { - "message": "Kimlik Doğrulayıcı, tarayıcınızda iki faktörlü kimlik doğrulama kodları oluşturur.", + "message": "OTPilot Authenticator, şifreli bulut yedekleme, siteye özel otomatik doldurma ve güvenlik önerileri sunarak iki faktörlü (2FA) kimlik doğrulama kodları oluşturur.", "description": "Extension Description." }, "added": { @@ -20,19 +28,23 @@ "description": "QR Error." }, "errorsecret": { - "message": "Geçersiz hesap anahtarı", + "message": "Geçersiz hesap gizli anahtarı", "description": "Secret Error." }, + "errorissuer": { + "message": "Uygulama adı \"::\" içeremez", + "description": "Issuer Error." + }, "add_code": { "message": "Hesap ekle", "description": "Add account." }, "add_qr": { - "message": "Karekodunu tara", + "message": "Karekod tara", "description": "Scan QR Code." }, "add_secret": { - "message": "Elle kayıt", + "message": "Elle giriş", "description": "Manual Entry." }, "migration_fail": { @@ -43,10 +55,6 @@ "message": "Bazı hesap verileri başarıyla içe aktarılamadı.", "description": "Some migration data is broken." }, - "close": { - "message": "Kapat", - "description": "Close." - }, "ok": { "message": "Tamam", "description": "OK." @@ -99,10 +107,6 @@ "message": "Mevcut parola", "description": "Current Passphrase." }, - "new_phrase": { - "message": "Yeni parola", - "description": "New Passphrase." - }, "phrase": { "message": "Parola", "description": "Passphrase." @@ -120,19 +124,15 @@ "description": "Message that user is required to acknowledge before clearing all data." }, "delete_all": { - "message": "Kimlik Doğrulayıcıyı sıfırla" + "message": "OTPilot'u sıfırla" }, "delete_all_warning": { - "message": "Bu işlem tüm verilerinizi silecek ve Kimlik Doğrulayıcıyı tamamen sıfırlayacaktır. Silinen verileri kurtaramazsınız. Kimlik Doğrulayıcıyı sıfırlamadan önce bir yedekleme kaydedin." + "message": "Bu işlem tüm verilerinizi silecek ve OTPilot'u tamamen sıfırlayacaktır. Silinen verileri kurtaramazsınız. OTPilot'u sıfırlamadan önce bir yedekleme kaydedin." }, "security_warning": { "message": "Bu parola hesaplarınızı şifrelemek için kullanılacaktır. Parolayı unutursanız kimse size yardım edemez.", "description": "Passphrase Warning." }, - "update": { - "message": "Güncelle", - "description": "Update." - }, "phrase_incorrect": { "message": "Tüm hesapların şifrelemesi çözülene kadar yeni bir hesap ekleyemezsiniz. Devam etmeden önce lütfen doğru parolayı girin.", "description": "Passphrase Incorrect." @@ -146,7 +146,7 @@ "description": "Passphrase Not Match." }, "encrypted": { - "message": "Şifrelendi", + "message": "Şifreli", "description": "Encrypted." }, "copied": { @@ -157,10 +157,6 @@ "message": "Geri bildirim", "description": "Feedback." }, - "translate": { - "message": "Tercüme et", - "description": "Translate." - }, "source": { "message": "Kaynak kodu", "description": "Source Code." @@ -173,10 +169,6 @@ "message": "Saati Google ile eşitle", "description": "Sync Clock" }, - "remember_phrase": { - "message": "Parolayı hatırla", - "description": "Remember Passphrase" - }, "clock_too_far_off": { "message": "Dikkat! Yerel saatiniz yanlış, lütfen devam etmeden önce düzeltin.", "description": "Local Time is Too Far Off" @@ -210,7 +202,7 @@ "description": "Scale" }, "export_info": { - "message": "Uyarı: Tüm yedeklemeler şifrelenmemiştir. Başka bir uygulamaya hesap eklemek ister misiniz? Herhangi bir hesabın sağ üst kısmındaki gizli karekod simgesine tıklayın.", + "message": "Uyarı: Tüm yedeklemeler şifrelenmemiştir. Başka bir uygulamaya hesap eklemek ister misiniz? Herhangi bir hesabın sağ üst kısmına gelin ve gizli düğmeye tıklayın.", "description": "Export menu info text" }, "download_backup": { @@ -234,13 +226,45 @@ "description": "Import qr images. Shown as add account method." }, "import_backup_code": { - "message": "Metin yedeklemeden içe aktar", + "message": "OTPAuth URI'sini içe aktar", "description": "Import backup code." }, "import_otp_urls": { "message": "OTP URL'lerini içe aktar", "description": "Import OTP URLs. Shown as add account method." }, + "import_drop_file": { + "message": "Yedekleme dosyasını buraya sürükleyin veya göz atmak için tıklayın", + "description": "File import dropzone title" + }, + "import_file_accepts": { + "message": ".json ve .txt dışa aktarımlarını kabul eder", + "description": "File import dropzone hint" + }, + "import_choose_file": { + "message": "Dosya seç", + "description": "File import dropzone button" + }, + "import_encrypted_note": { + "message": "Dosya şifrelenmişse, seçtikten sonra parolası sorulacaktır.", + "description": "File import note about encrypted backups" + }, + "import_enc_required": { + "message": "Şifreli — parola gerekli", + "description": "Badge on a picked encrypted backup file" + }, + "import_pass_placeholder": { + "message": "Dosyanın parolasını girin", + "description": "Passphrase input placeholder for encrypted import" + }, + "import_pass_hint": { + "message": "Bu, yedeklemeyi oluştururken belirlediğiniz paroladır.", + "description": "Hint under the import passphrase field" + }, + "import_decrypt": { + "message": "Şifresini çöz ve içe aktar", + "description": "Button to decrypt and import an encrypted backup" + }, "import_backup_qr_partly_failed": { "message": "İçe aktarma başarılı ancak bazı karekodlar tanınamadı.", "description": "Import successful, but some QR image cannot be recognized." @@ -290,21 +314,113 @@ "description": "Edit" }, "manual_dropbox": { - "message": "Elle yedekle", + "message": "Elle yedekleme", "description": "Manual backup" }, "use_autofill": { "message": "Otomatik doldurmayı kullanın", "description": "Use Autofill" }, - "use_high_contrast": { - "message": "Yüksek kontrast kullan", - "description": "Use High Contrast" - }, "theme": { "message": "Tema", "description": "Theme" }, + "settings_appearance": { + "message": "Görünüm", + "description": "Settings section: appearance" + }, + "settings_general": { + "message": "Genel", + "description": "Settings section: general" + }, + "edit_accounts": { + "message": "Hesapları düzenle", + "description": "Header title while in edit mode" + }, + "show_qr": { + "message": "Karekodu göster", + "description": "Context-menu action to show an account's QR code" + }, + "pin_to_top": { + "message": "En üste sabitle", + "description": "Context-menu action to pin an account to the top" + }, + "unpin": { + "message": "Sabitlemeyi kaldır", + "description": "Context-menu action to unpin an account" + }, + "vault_locked": { + "message": "Kasa kilitli", + "description": "Title on the unlock screen" + }, + "unlock": { + "message": "Kilidi aç", + "description": "Unlock button" + }, + "qr_transfer_title": { + "message": "Bu hesabı aktar", + "description": "QR sheet caption title" + }, + "qr_transfer_desc": { + "message": "Bu hesabı aktarmak için başka bir cihazda OTPilot ile bu kodu tarayın — veriler cihazlarınızdan hiç ayrılmaz.", + "description": "QR sheet caption body" + }, + "backup_on_device": { + "message": "Bu cihazda", + "description": "Backup section: local" + }, + "backup_cloud_sync": { + "message": "Bulut eşitleme", + "description": "Backup section: cloud" + }, + "backup_connected": { + "message": "Bağlandı", + "description": "Cloud provider connected status" + }, + "backup_not_connected": { + "message": "Bağlı değil", + "description": "Cloud provider not-connected status" + }, + "showing_codes_for": { + "message": "Gösterilen kodlar:", + "description": "Smart-filter banner prefix, followed by the site domain" + }, + "other_accounts": { + "message": "Diğer hesaplar", + "description": "Divider above non-matching accounts in the filter view" + }, + "filter_to_site": { + "message": "Filtrele:", + "description": "Banner prefix to re-apply the site filter, followed by the domain" + }, + "onboarding_title": { + "message": "Sizinle birlikte giden kodlar", + "description": "Onboarding headline" + }, + "onboarding_subtitle": { + "message": "Her hesap için güvenli iki faktörlü kodlar oluşturun — şifreli, çevrimdışı ve kopyalamaya her zaman bir dokunuş uzaklıkta.", + "description": "Onboarding subtitle" + }, + "onboarding_feature_offline": { + "message": "Tamamen çevrimdışı çalışır — hiçbir şey cihazınızdan ayrılmaz", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_encrypted": { + "message": "İsteğe bağlı parola kilidiyle şifreli kasa", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_scan": { + "message": "Karekod tarayın veya mevcut bir yedeklemeyi içe aktarın", + "description": "Onboarding feature bullet" + }, + "onboarding_get_started": { + "message": "Başlayın", + "description": "Onboarding primary button" + }, + "onboarding_import": { + "message": "İçe aktaracağım bir yedeklemem var", + "description": "Onboarding secondary button" + }, "theme_light": { "message": "Açık", "description": "Light theme" @@ -313,26 +429,18 @@ "message": "Karanlık", "description": "Dark theme" }, - "theme_simple": { - "message": "Düz", - "description": "Simple theme" + "theme_auto": { + "message": "Otomatik", + "description": "Follow the system light/dark setting" }, "theme_compact": { - "message": "Yoğun", + "message": "Kompakt", "description": "Compact theme" }, "theme_high_contrast": { "message": "Yüksek kontrast", "description": "High Contrast theme" }, - "theme_flat": { - "message": "Mat", - "description": "Flat theme" - }, - "storage_sync_info": { - "message": "Verilerinizi otomatik olarak 3. taraf depolama servislerine yedekleyin.", - "description": "3rd party backup info" - }, "browser_sync": { "message": "Tarayıcı eşitlemesi", "description": "Storage location" @@ -362,10 +470,6 @@ } } }, - "otp_unsupported_warn": { - "message": "Bir veya daha fazla Steam veya Blizzard hesabınız var. Şifrelenmemiş yedeklemeler standart yedekleme formatını kullanmayacaktır.", - "description": "Warning if using account that is not supported by standard backup format." - }, "otp_backup_inform": { "message": "Başka uygulamalardan da yedekleri içe aktarabilirsiniz.", "description": "Info text on import page" @@ -378,7 +482,7 @@ "message": "Yükleniyor..." }, "autolock": { - "message": "Sonrasında kilitle" + "message": "Kilitleme süresi" }, "minutes": { "message": "dakika" @@ -405,10 +509,7 @@ "message": "Akıllı filtre" }, "backup": { - "message": "Yedekle" - }, - "backup_file_info": { - "message": "Verilerinizi bir dosyaya yedekleyin." + "message": "Yedekleme" }, "password_policy_default_hint": { "message": "Parolanız kuruluşunuzun güvenlik gereksinimlerini karşılamıyor. Daha fazla bilgi için yöneticinize başvurun." @@ -447,7 +548,7 @@ "message": "Bilgi" }, "dismiss": { - "message": "Reddet" + "message": "Kapat" }, "learn_more": { "message": "Daha fazla bilgi edinin" @@ -459,19 +560,19 @@ "message": "Görüntülenecek hesap yok. İlk hesabınızı şimdi ekleyin." }, "permissions": { - "message": "Yetkiler" + "message": "İzinler" }, "permission_revoke": { "message": "İptal et" }, "permission_show_required_permissions": { - "message": "Geri alınamayan yekileri göster" + "message": "Geri alınamayan izinleri göster" }, "permission_required": { - "message": "Bu gerekli bir yetkidir ve iptal edilemez." + "message": "Bu gerekli bir izindir ve iptal edilemez." }, "permission_active_tab": { - "message": "Karekodlarını taramak için geçerli sekmeye erişim." + "message": "Karekodları taramak için geçerli sekmeye erişim." }, "permission_storage": { "message": "Hesap verilerini saklamak için tarayıcı depolama alanına erişim." @@ -483,13 +584,13 @@ "message": "Otomatik kilitlemenin çalışmasını sağlar." }, "permission_scripting": { - "message": "Karekodları taramak ve otomatik doldurmanın çalışmasını sağlamak için geçerli sekmeye komut dosyaları yerleştirin." + "message": "Karekodları taramak ve otomatik doldurmanın çalışmasını sağlamak için geçerli sekmeye komut dosyaları yerleştirir." }, "permission_clipboard_write": { "message": "Hesaba tıkladığınızda kodları panoya kopyalamak için panoya yalnızca yazma erişimi verir." }, "permission_context_menus": { - "message": "İçerik menüsüne Kimlik Doğrulayıcıyı ekler." + "message": "İçerik menüsüne OTPilot'u ekler." }, "permission_sync_clock": { "message": "Google ile saat eşitlemesine izin verir." @@ -500,25 +601,17 @@ "permission_dropbox_cannot_revoke": { "message": "Önce Dropbox yedeklemesini devre dışı bırakmalısınız." }, - "permission_drive": { - "message": "Google Drive'a yedeklemeye izin verir." - }, - "permission_drive_cannot_revoke": { - "message": "Önce Google Drive yedeklemesini devre dışı bırakmalısınız." - }, - "permission_onedrive": { - "message": "OneDrive'a yedeklemeye izin verir." - }, - "permission_onedrive_cannot_revoke": { - "message": "Önce OneDrive yedeklemesini devre dışı bırakmalısınız." - }, "permission_unknown_permission": { - "message": "Bilinmeyen yetki. Bu mesajı görürseniz bir hata raporu gönderin." + "message": "Bilinmeyen izin. Bu mesajı görürseniz bir hata raporu gönderin." }, "phrase_wrong": { - "message": "Password incorrect" + "message": "Parola yanlış" }, "activate_auto_filter": { - "message": "Warning: Smart filter loosely matches the domain name to an account. Always verify that you are on the correct website before entering a code!" + "message": "Uyarı: Akıllı filtre, alan adını bir hesapla gevşek biçimde eşleştirir. Kod girmeden önce her zaman doğru web sitesinde olduğunuzdan emin olun!" + }, + "backup_unavailable": { + "message": "Henüz kullanılamıyor", + "description": "Shown on a cloud provider that is not implemented yet." } } diff --git a/_locales/uk/messages.json b/_locales/uk/messages.json index 76c3972c1..b5dc4801d 100644 --- a/_locales/uk/messages.json +++ b/_locales/uk/messages.json @@ -1,18 +1,26 @@ { "extName": { - "message": "Authenticator", + "message": "OTPilot Authenticator", "description": "Extension Name." }, "extShortName": { - "message": "Authenticator", + "message": "OTPilot", "description": "Extension Short Name." }, + "host": { + "message": "Сайт", + "description": "Bound website host used to restrict autofill to a matching page." + }, + "backup_requires_password": { + "message": "Спочатку встановіть пароль для використання хмарного резервного копіювання, щоб ваші секрети шифрувалися ще до того, як залишать цей пристрій.", + "description": "Shown in the cloud backup section when no master password is set." + }, "extDesc": { - "message": "Автентифікатор генерує коди двофакторної аутентифікації у вашому браузері.", + "message": "OTPilot Authenticator генерує коди двофакторної автентифікації (2FA), має зашифроване хмарне резервне копіювання, автозаповнення з прив'язкою до сайту та поради з безпеки.", "description": "Extension Description." }, "added": { - "message": " був доданий.", + "message": " додано.", "description": "Added Account." }, "errorqr": { @@ -20,9 +28,13 @@ "description": "QR Error." }, "errorsecret": { - "message": "Неприпустимий ідентифікатор аккаунта", + "message": "Недійсний секретний ключ облікового запису", "description": "Secret Error." }, + "errorissuer": { + "message": "Видавець не може містити \"::\"", + "description": "Issuer Error." + }, "add_code": { "message": "Додати обліковий запис", "description": "Add account." @@ -40,13 +52,9 @@ "description": "Import migration data failed." }, "migration_partly_fail": { - "message": "Частина данних облікового запису не була імпортована.", + "message": "Частина даних облікового запису не була імпортована.", "description": "Some migration data is broken." }, - "close": { - "message": "Закрити", - "description": "Close." - }, "ok": { "message": "ОК", "description": "OK." @@ -60,7 +68,7 @@ "description": "No." }, "account": { - "message": "Konto", + "message": "Обліковий запис", "description": "Account." }, "accountName": { @@ -76,7 +84,7 @@ "description": "Secret." }, "updateSuccess": { - "message": "Success.", + "message": "Успіх.", "description": "Update Success." }, "updateFailure": { @@ -99,10 +107,6 @@ "message": "Поточний пароль", "description": "Current Passphrase." }, - "new_phrase": { - "message": "Новий пароль", - "description": "New Passphrase." - }, "phrase": { "message": "Пароль", "description": "Passphrase." @@ -112,37 +116,33 @@ "description": "Confirm Passphrase." }, "confirm_delete": { - "message": "Ви впевнені, що хочете видалити цей обліковий запис? Цю дію не можливо буде скасувати.", + "message": "Ви впевнені, що хочете видалити цей обліковий запис? Цю дію неможливо буде скасувати.", "description": "Remove entry confirmation" }, "confirm_delete_all": { - "message": "Я розумію, що всі мої дані буде невиправно видалено.", + "message": "Я розумію, що всі мої дані буде безповоротно видалено.", "description": "Message that user is required to acknowledge before clearing all data." }, "delete_all": { - "message": "Скинути автентифікатор" + "message": "Скинути OTPilot" }, "delete_all_warning": { - "message": "Це призведе до видалення всіх Ваших даних і повного скидання автентифікатора. Ви не зможете відновити втрачені дані! Вам слід розглянути можливість збереження резервної копії перед скиданням автентифікатора." + "message": "Це призведе до видалення всіх ваших даних та повного скидання OTPilot. Ви не зможете відновити жодні видалені дані! Вам варто зробити резервну копію перед скиданням OTPilot." }, "security_warning": { "message": "Цей пароль буде використовуватися для шифрування ваших облікових записів. Ніхто не зможе допомогти вам, якщо ви забудете пароль.", "description": "Passphrase Warning." }, - "update": { - "message": "Оновити", - "description": "Update." - }, "phrase_incorrect": { "message": "Ви не можете додати новий обліковий запис, поки всі облікові записи не будуть розшифровані. Будь ласка, введіть правильний пароль перед продовженням.", "description": "Passphrase Incorrect." }, "phrase_incorrect_export": { - "message": "Облікові записи, які не змогли розшифруватися, не будуть включені до цієї резервної копії.", + "message": "Облікові записи, які не вдалося розшифрувати, не будуть включені до цієї резервної копії.", "description": "Skip Unable-decripted Data." }, "phrase_not_match": { - "message": "Пароль не підходить.", + "message": "Пароль не збігається.", "description": "Passphrase Not Match." }, "encrypted": { @@ -157,10 +157,6 @@ "message": "Відгук", "description": "Feedback." }, - "translate": { - "message": "Переклад", - "description": "Translate." - }, "source": { "message": "Вихідний код", "description": "Source Code." @@ -173,16 +169,12 @@ "message": "Синхронізувати годинник з Google", "description": "Sync Clock" }, - "remember_phrase": { - "message": "Запам'ятати пароль", - "description": "Remember Passphrase" - }, "clock_too_far_off": { - "message": "Увага! Ваш місцевий годинник налаштовано неправильно. Будь ласка, виправте його, перш ніж продовжити.", + "message": "Увага! Ваш локальний годинник значно розходиться з точним часом. Будь ласка, виправте його, перш ніж продовжити.", "description": "Local Time is Too Far Off" }, "remind_backup": { - "message": "У вас є резервні копії ваших облікових записів? Зробіть зараз. Не чекайте, доки це буде занадто пізно!", + "message": "У вас є резервна копія ваших облікових записів? Не чекайте, доки буде занадто пізно!", "description": "Remind Backup" }, "capture_failed": { @@ -190,7 +182,7 @@ "description": "Capture Failed" }, "capture_local_file_failed": { - "message": "Are you trying to scan QR code from a local file? Use Import QR Image Backup instead.", + "message": "Ви намагаєтеся сканувати QR-код із локального файлу? Замість цього скористайтеся функцією «Імпортувати резервну копію QR-зображення».", "description": "Import QR image backup instead of scan local image" }, "based_on_time": { @@ -210,7 +202,7 @@ "description": "Scale" }, "export_info": { - "message": "Попередження: усі резервні копії не зашифровані. Хочете додати акаунт в інший додаток? Наведіть курсор на праву частину будь-якого облікового запису і натисніть приховану кнопку.", + "message": "Попередження: усі резервні копії не зашифровані. Хочете додати обліковий запис до іншого додатка? Наведіть курсор на верхню праву частину будь-якого облікового запису і натисніть приховану кнопку.", "description": "Export menu info text" }, "download_backup": { @@ -234,13 +226,45 @@ "description": "Import qr images. Shown as add account method." }, "import_backup_code": { - "message": "Імпорт та резервні копії з тексту", + "message": "Імпорт OTPAuth URI", "description": "Import backup code." }, "import_otp_urls": { - "message": "Імпорт URL-адрес OTP", + "message": "Імпорт OTPAuth URI", "description": "Import OTP URLs. Shown as add account method." }, + "import_drop_file": { + "message": "Перетягніть файл резервної копії або натисніть, щоб вибрати", + "description": "File import dropzone title" + }, + "import_file_accepts": { + "message": "Підтримуються експорти .json та .txt", + "description": "File import dropzone hint" + }, + "import_choose_file": { + "message": "Вибрати файл", + "description": "File import dropzone button" + }, + "import_encrypted_note": { + "message": "Якщо файл зашифровано, після вибору вас попросять ввести пароль.", + "description": "File import note about encrypted backups" + }, + "import_enc_required": { + "message": "Зашифровано — потрібен пароль", + "description": "Badge on a picked encrypted backup file" + }, + "import_pass_placeholder": { + "message": "Введіть пароль файлу", + "description": "Passphrase input placeholder for encrypted import" + }, + "import_pass_hint": { + "message": "Це пароль, який ви встановили під час створення резервної копії.", + "description": "Hint under the import passphrase field" + }, + "import_decrypt": { + "message": "Розшифрувати та імпортувати", + "description": "Button to decrypt and import an encrypted backup" + }, "import_backup_qr_partly_failed": { "message": "Імпорт завершено, але деякі QR-коди не вдалося розпізнати.", "description": "Import successful, but some QR image cannot be recognized." @@ -297,14 +321,106 @@ "message": "Використовувати автозаповнення", "description": "Use Autofill" }, - "use_high_contrast": { - "message": "Використовувати високу контрастність", - "description": "Use High Contrast" - }, "theme": { "message": "Тема", "description": "Theme" }, + "settings_appearance": { + "message": "Зовнішній вигляд", + "description": "Settings section: appearance" + }, + "settings_general": { + "message": "Загальні", + "description": "Settings section: general" + }, + "edit_accounts": { + "message": "Редагувати облікові записи", + "description": "Header title while in edit mode" + }, + "show_qr": { + "message": "Показати QR-код", + "description": "Context-menu action to show an account's QR code" + }, + "pin_to_top": { + "message": "Закріпити вгорі", + "description": "Context-menu action to pin an account to the top" + }, + "unpin": { + "message": "Відкріпити", + "description": "Context-menu action to unpin an account" + }, + "vault_locked": { + "message": "Сховище заблоковано", + "description": "Title on the unlock screen" + }, + "unlock": { + "message": "Розблокувати", + "description": "Unlock button" + }, + "qr_transfer_title": { + "message": "Перенести цей обліковий запис", + "description": "QR sheet caption title" + }, + "qr_transfer_desc": { + "message": "Відскануйте цей код у OTPilot на іншому пристрої, щоб перенести цей обліковий запис — дані ніколи не залишають ваші пристрої.", + "description": "QR sheet caption body" + }, + "backup_on_device": { + "message": "На цьому пристрої", + "description": "Backup section: local" + }, + "backup_cloud_sync": { + "message": "Хмарна синхронізація", + "description": "Backup section: cloud" + }, + "backup_connected": { + "message": "Підключено", + "description": "Cloud provider connected status" + }, + "backup_not_connected": { + "message": "Не підключено", + "description": "Cloud provider not-connected status" + }, + "showing_codes_for": { + "message": "Показано коди для", + "description": "Smart-filter banner prefix, followed by the site domain" + }, + "other_accounts": { + "message": "Інші облікові записи", + "description": "Divider above non-matching accounts in the filter view" + }, + "filter_to_site": { + "message": "Фільтр за", + "description": "Banner prefix to re-apply the site filter, followed by the domain" + }, + "onboarding_title": { + "message": "Коди, які завжди з вами", + "description": "Onboarding headline" + }, + "onboarding_subtitle": { + "message": "Генеруйте надійні коди двофакторної автентифікації для кожного облікового запису — зашифровано, офлайн і завжди на відстані одного дотику для копіювання.", + "description": "Onboarding subtitle" + }, + "onboarding_feature_offline": { + "message": "Повністю офлайн-робота — нічого не залишає ваш пристрій", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_encrypted": { + "message": "Зашифроване сховище з опціональним захистом паролем", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_scan": { + "message": "Скануйте QR-код або імпортуйте наявну резервну копію", + "description": "Onboarding feature bullet" + }, + "onboarding_get_started": { + "message": "Почати", + "description": "Onboarding primary button" + }, + "onboarding_import": { + "message": "У мене є резервна копія для імпорту", + "description": "Onboarding secondary button" + }, "theme_light": { "message": "Світла", "description": "Light theme" @@ -313,28 +429,20 @@ "message": "Темна", "description": "Dark theme" }, - "theme_simple": { - "message": "Проста", - "description": "Simple theme" + "theme_auto": { + "message": "Автоматична", + "description": "Follow the system light/dark setting" }, "theme_compact": { - "message": "Компактне", + "message": "Компактна", "description": "Compact theme" }, "theme_high_contrast": { "message": "Висока контрастність", "description": "High Contrast theme" }, - "theme_flat": { - "message": "Фіат", - "description": "Flat theme" - }, - "storage_sync_info": { - "message": "Автоматично створювати резервні копії Ваших даних у сторонніх сервісах-сховищах.", - "description": "3rd party backup info" - }, "browser_sync": { - "message": "Browser Sync", + "message": "Синхронізація браузера", "description": "Storage location" }, "sign_in": { @@ -362,10 +470,6 @@ } } }, - "otp_unsupported_warn": { - "message": "У вас є один або кілька облікових записів Steam або Blizzard. Незашифровані резервні копії не будуть використовувати стандартизований формат.", - "description": "Warning if using account that is not supported by standard backup format." - }, "otp_backup_inform": { "message": "Ви можете імпортувати резервні копії з деяких інших програм.", "description": "Info text on import page" @@ -405,10 +509,7 @@ "message": "Розумний фільтр" }, "backup": { - "message": "Backup" - }, - "backup_file_info": { - "message": "Backup your data to a file." + "message": "Резервне копіювання" }, "password_policy_default_hint": { "message": "Ваш пароль не відповідає вимогам безпеки вашої організації. Для отримання додаткової інформації зверніться до адміністратора." @@ -426,19 +527,19 @@ "message": "Синхронізацію браузера вимкнено. Увімкнення дозволяє синхронізувати облікові записи між браузерами." }, "advisor_insight_auto_fill_not_enabled": { - "message": "Увімкнути автозаповнення для автоматичного заповнення кодів на веб-сайтах." + "message": "Увімкнення автозаповнення дозволяє автоматично заповнювати коди на вебсайтах." }, "advisor_insight_smart_filter_not_enabled": { "message": "Увімкнення розумного фільтра дозволяє отримати швидкий доступ до облікових записів." }, "show_all_insights": { - "message": "Показати всі деталі." + "message": "Показати всі відомості." }, "no_insight_available": { "message": "Не знайдено жодних відомостей, все виглядає добре!" }, "danger": { - "message": "Загроза" + "message": "Небезпека" }, "warning": { "message": "Попередження" @@ -456,13 +557,13 @@ "message": "Додати до контекстного меню" }, "no_entires": { - "message": "Немає аккаунтів, щоб їх показати. Додайте свій перший аккаунт зараз." + "message": "Немає облікових записів для відображення. Додайте свій перший обліковий запис зараз." }, "permissions": { "message": "Дозволи" }, "permission_revoke": { - "message": "Revoke" + "message": "Відкликати" }, "permission_show_required_permissions": { "message": "Показати дозволи, які не можна відкликати" @@ -483,13 +584,13 @@ "message": "Дозволяє роботу автоматичного блокування." }, "permission_scripting": { - "message": "Вставте сценарії в поточну вкладку, щоб сканувати QR-коди та дозволити роботу автозаповнення." + "message": "Вставляє сценарії в поточну вкладку для сканування QR-кодів та роботи автозаповнення." }, "permission_clipboard_write": { - "message": "Надає доступ лише до запису до буфера обміну для копіювання кодів в буфер обміну, коли ви клацаєте на нього в обліковому записі." + "message": "Надає доступ лише для запису в буфер обміну, щоб копіювати коди до буфера обміну під час натискання на обліковий запис." }, "permission_context_menus": { - "message": "Додає автентифікатор до контекстного меню." + "message": "Додає OTPilot до контекстного меню." }, "permission_sync_clock": { "message": "Дозволяє синхронізувати годинник з Google." @@ -500,25 +601,17 @@ "permission_dropbox_cannot_revoke": { "message": "Спочатку необхідно вимкнути резервне копіювання Dropbox." }, - "permission_drive": { - "message": "Дозволяє створювати резервні копії на Google Drive." - }, - "permission_drive_cannot_revoke": { - "message": "Спочатку необхідно вимкнути резервне копіювання Google Drive." - }, - "permission_onedrive": { - "message": "Дозволяє створювати резервні копії на OneDrive." - }, - "permission_onedrive_cannot_revoke": { - "message": "Спочатку необхідно вимкнути резервне копіювання OneDrive." - }, "permission_unknown_permission": { "message": "Невідомий дозвіл. Якщо ви бачите це повідомлення, будь ласка, надішліть повідомлення про помилку." }, "phrase_wrong": { - "message": "Пароль не правильний" + "message": "Пароль неправильний" }, "activate_auto_filter": { - "message": "Попередження: Розумний фільтр неточно співставляє доменне ім’я з акаунтом. Завжди перевіряйте, що ви перебуваєте на правильному вебсайті, перш ніж вводити код!" + "message": "Попередження: розумний фільтр неточно зіставляє доменне ім'я з обліковим записом. Завжди перевіряйте, що ви перебуваєте на правильному вебсайті, перш ніж вводити код!" + }, + "backup_unavailable": { + "message": "Поки що недоступно", + "description": "Shown on a cloud provider that is not implemented yet." } } diff --git a/_locales/vi/messages.json b/_locales/vi/messages.json index e35f6e85f..a4bb3fcd0 100644 --- a/_locales/vi/messages.json +++ b/_locales/vi/messages.json @@ -1,14 +1,22 @@ { "extName": { - "message": "Authenticator", + "message": "OTPilot Authenticator", "description": "Extension Name." }, "extShortName": { - "message": "Trình xác thực", + "message": "OTPilot", "description": "Extension Short Name." }, + "host": { + "message": "Trang web", + "description": "Bound website host used to restrict autofill to a matching page." + }, + "backup_requires_password": { + "message": "Vui lòng đặt mật khẩu trước khi sử dụng sao lưu đám mây, để dữ liệu bí mật của bạn được mã hóa trước khi rời khỏi thiết bị này.", + "description": "Shown in the cloud backup section when no master password is set." + }, "extDesc": { - "message": "Authenticator tạo mã xác thực hai yếu tố trong trình duyệt của bạn", + "message": "OTPilot Authenticator tạo mã xác thực hai yếu tố (2FA), có sao lưu đám mây được mã hóa, tự động điền theo trang web và gợi ý bảo mật.", "description": "Extension Description." }, "added": { @@ -20,9 +28,13 @@ "description": "QR Error." }, "errorsecret": { - "message": "Lỗi bảo mật không hợp lệ", + "message": "Mã bí mật tài khoản không hợp lệ", "description": "Secret Error." }, + "errorissuer": { + "message": "Nhà phát hành không được chứa \"::\"", + "description": "Issuer Error." + }, "add_code": { "message": "Thêm tài khoản", "description": "Add account." @@ -43,10 +55,6 @@ "message": "Một vài dữ liệu tài khoản không nhập vào thành công.", "description": "Some migration data is broken." }, - "close": { - "message": "Đóng", - "description": "Close." - }, "ok": { "message": "Xác nhận", "description": "OK." @@ -99,10 +107,6 @@ "message": "Mật khẩu hiện tại", "description": "Current Passphrase." }, - "new_phrase": { - "message": "Mật khẩu mới", - "description": "New Passphrase." - }, "phrase": { "message": "Mật khẩu", "description": "Passphrase." @@ -112,7 +116,7 @@ "description": "Confirm Passphrase." }, "confirm_delete": { - "message": "Bạn có chắc sẽ xóa tài khoản này? Yêu cầu này không thể hoàn tác.", + "message": "Bạn có chắc chắn muốn xóa tài khoản này không? Hành động này không thể hoàn tác.", "description": "Remove entry confirmation" }, "confirm_delete_all": { @@ -120,25 +124,21 @@ "description": "Message that user is required to acknowledge before clearing all data." }, "delete_all": { - "message": "Thiết lập lại Authenticator" + "message": "Thiết lập lại OTPilot" }, "delete_all_warning": { - "message": "Hành động này sẽ xóa toàn bộ dữ liệu của bạn và thiết lập Authenticator về trạng thái ban đầu. Bạn sẽ không thể khôi phục dữ liệu đã xóa. Bạn nên cân nhắc việc sao lưu lại dữ liệu trước khi đặt lại Authenticator." + "message": "Hành động này sẽ xóa toàn bộ dữ liệu của bạn và thiết lập OTPilot về trạng thái ban đầu. Bạn sẽ không thể khôi phục dữ liệu đã xóa. Bạn nên cân nhắc việc sao lưu lại dữ liệu trước khi đặt lại OTPilot." }, "security_warning": { "message": "Mật khẩu này sẽ được sử dụng để mã hóa tài khoản của bạn. Không ai có thể giúp bạn nếu bạn quên mật khẩu.", "description": "Passphrase Warning." }, - "update": { - "message": "Cập nhật", - "description": "Update." - }, "phrase_incorrect": { - "message": "You cannot add a new account until all accounts are decrypted. Please enter the correct password before continuing.", + "message": "Bạn không thể thêm tài khoản mới cho đến khi tất cả tài khoản đã được giải mã. Vui lòng nhập đúng mật khẩu trước khi tiếp tục.", "description": "Passphrase Incorrect." }, "phrase_incorrect_export": { - "message": "Accounts that were not able to be decrypted will not be included in this backup.", + "message": "Các tài khoản không thể giải mã sẽ không được đưa vào bản sao lưu này.", "description": "Skip Unable-decripted Data." }, "phrase_not_match": { @@ -157,10 +157,6 @@ "message": "Phản hồi", "description": "Feedback." }, - "translate": { - "message": "Dịch thuật", - "description": "Translate." - }, "source": { "message": "Mã nguồn", "description": "Source Code." @@ -173,10 +169,6 @@ "message": "Đồng bộ đồng hồ với Google", "description": "Sync Clock" }, - "remember_phrase": { - "message": "Nhớ mật khẩu", - "description": "Remember Passphrase" - }, "clock_too_far_off": { "message": "Chú ý! Đồng hồ trên máy của bạn bị sai, xin hãy điều chỉnh lại trước khi tiếp tục.", "description": "Local Time is Too Far Off" @@ -190,7 +182,7 @@ "description": "Capture Failed" }, "capture_local_file_failed": { - "message": "Bạn có thử quét mã QR từ file trên máy của bạn? Sử dụng ảnh chứa mã QR để thay thế.", + "message": "Bạn đang thử quét mã QR từ tệp trên máy tính? Hãy dùng chức năng Nhập sao lưu hình ảnh QR để thay thế.", "description": "Import QR image backup instead of scan local image" }, "based_on_time": { @@ -230,17 +222,49 @@ "description": "Import qr image backup." }, "import_qr_images": { - "message": "Import QR Images", + "message": "Nhập hình ảnh QR", "description": "Import qr images. Shown as add account method." }, "import_backup_code": { - "message": "Nhập bản sao lưu", + "message": "Nhập OTPAuth URI", "description": "Import backup code." }, "import_otp_urls": { "message": "Nhập các đường liên kết chứa OTP", "description": "Import OTP URLs. Shown as add account method." }, + "import_drop_file": { + "message": "Kéo thả tệp sao lưu vào đây, hoặc nhấn để duyệt tệp", + "description": "File import dropzone title" + }, + "import_file_accepts": { + "message": "Hỗ trợ tệp xuất .json và .txt", + "description": "File import dropzone hint" + }, + "import_choose_file": { + "message": "Chọn tệp", + "description": "File import dropzone button" + }, + "import_encrypted_note": { + "message": "Nếu tệp đã được mã hóa, bạn sẽ được yêu cầu nhập mật khẩu sau khi chọn tệp.", + "description": "File import note about encrypted backups" + }, + "import_enc_required": { + "message": "Đã mã hóa — cần mật khẩu", + "description": "Badge on a picked encrypted backup file" + }, + "import_pass_placeholder": { + "message": "Nhập mật khẩu của tệp", + "description": "Passphrase input placeholder for encrypted import" + }, + "import_pass_hint": { + "message": "Đây là mật khẩu bạn đã đặt khi tạo bản sao lưu này.", + "description": "Hint under the import passphrase field" + }, + "import_decrypt": { + "message": "Giải mã & nhập", + "description": "Button to decrypt and import an encrypted backup" + }, "import_backup_qr_partly_failed": { "message": "Nhập thành công, nhưng không thể nhận dạng một số hình ảnh QR.", "description": "Import successful, but some QR image cannot be recognized." @@ -297,14 +321,106 @@ "message": "Sử dụng tự động điền", "description": "Use Autofill" }, - "use_high_contrast": { - "message": "Độ tương phản cao", - "description": "Use High Contrast" - }, "theme": { "message": "Chủ đề", "description": "Theme" }, + "settings_appearance": { + "message": "Giao diện", + "description": "Settings section: appearance" + }, + "settings_general": { + "message": "Chung", + "description": "Settings section: general" + }, + "edit_accounts": { + "message": "Sửa tài khoản", + "description": "Header title while in edit mode" + }, + "show_qr": { + "message": "Hiển thị mã QR", + "description": "Context-menu action to show an account's QR code" + }, + "pin_to_top": { + "message": "Ghim lên đầu", + "description": "Context-menu action to pin an account to the top" + }, + "unpin": { + "message": "Bỏ ghim", + "description": "Context-menu action to unpin an account" + }, + "vault_locked": { + "message": "Kho dữ liệu đã khóa", + "description": "Title on the unlock screen" + }, + "unlock": { + "message": "Mở khóa", + "description": "Unlock button" + }, + "qr_transfer_title": { + "message": "Chuyển tài khoản này", + "description": "QR sheet caption title" + }, + "qr_transfer_desc": { + "message": "Quét mã này bằng OTPilot trên thiết bị khác để chuyển tài khoản này sang — dữ liệu không bao giờ rời khỏi thiết bị của bạn.", + "description": "QR sheet caption body" + }, + "backup_on_device": { + "message": "Trên thiết bị này", + "description": "Backup section: local" + }, + "backup_cloud_sync": { + "message": "Đồng bộ đám mây", + "description": "Backup section: cloud" + }, + "backup_connected": { + "message": "Đã kết nối", + "description": "Cloud provider connected status" + }, + "backup_not_connected": { + "message": "Chưa kết nối", + "description": "Cloud provider not-connected status" + }, + "showing_codes_for": { + "message": "Đang hiển thị mã cho", + "description": "Smart-filter banner prefix, followed by the site domain" + }, + "other_accounts": { + "message": "Tài khoản khác", + "description": "Divider above non-matching accounts in the filter view" + }, + "filter_to_site": { + "message": "Lọc theo", + "description": "Banner prefix to re-apply the site filter, followed by the domain" + }, + "onboarding_title": { + "message": "Mã xác thực luôn bên bạn", + "description": "Onboarding headline" + }, + "onboarding_subtitle": { + "message": "Tạo mã xác thực hai yếu tố an toàn cho mọi tài khoản — được mã hóa, hoạt động ngoại tuyến, chỉ cần một chạm để sao chép.", + "description": "Onboarding subtitle" + }, + "onboarding_feature_offline": { + "message": "Hoạt động hoàn toàn ngoại tuyến — không có gì rời khỏi thiết bị của bạn", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_encrypted": { + "message": "Kho dữ liệu được mã hóa với khóa mật khẩu tùy chọn", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_scan": { + "message": "Quét mã QR hoặc nhập bản sao lưu có sẵn", + "description": "Onboarding feature bullet" + }, + "onboarding_get_started": { + "message": "Bắt đầu", + "description": "Onboarding primary button" + }, + "onboarding_import": { + "message": "Tôi đã có bản sao lưu để nhập", + "description": "Onboarding secondary button" + }, "theme_light": { "message": "Sáng", "description": "Light theme" @@ -313,9 +429,9 @@ "message": "Tối", "description": "Dark theme" }, - "theme_simple": { - "message": "Đơn giản", - "description": "Simple theme" + "theme_auto": { + "message": "Tự động", + "description": "Follow the system light/dark setting" }, "theme_compact": { "message": "Rút gọn", @@ -325,14 +441,6 @@ "message": "Tương phản cao", "description": "High Contrast theme" }, - "theme_flat": { - "message": "Căn hộ", - "description": "Flat theme" - }, - "storage_sync_info": { - "message": "Tự động sao lưu dữ liệu của bạn với dịch vụ lưu trữ bên thứ 3.", - "description": "3rd party backup info" - }, "browser_sync": { "message": "Đồng bộ trình duyệt", "description": "Storage location" @@ -342,7 +450,7 @@ "description": "Sign in to 3rd party storage services" }, "sign_in_business": { - "message": "Đăng nhập", + "message": "Đăng nhập (Doanh nghiệp)", "description": "Sign in to 3rd party storage services" }, "onedrive_business_perms": { @@ -362,10 +470,6 @@ } } }, - "otp_unsupported_warn": { - "message": "Bạn có một hoặc nhiều tài khoản Steam hoặc Blizzard. Các bản sao lưu chưa được mã hóa sẽ không sử dụng định dạng sao lưu tiêu chuẩn.", - "description": "Warning if using account that is not supported by standard backup format." - }, "otp_backup_inform": { "message": "Bạn có thể nhập bản sao lưu từ các ứng dụng khác.", "description": "Info text on import page" @@ -375,7 +479,7 @@ "description": "learn more link on import page. Placed after otp_backup_inform" }, "loading": { - "message": "Đang tải ..." + "message": "Đang tải..." }, "autolock": { "message": "Khóa sau" @@ -387,7 +491,7 @@ "message": "Nâng cao" }, "period": { - "message": "Thời hạn" + "message": "Chu kỳ" }, "type": { "message": "Loại" @@ -396,7 +500,7 @@ "message": "Không hợp lệ" }, "digits": { - "message": "Đồng hồ số" + "message": "Chữ số" }, "algorithm": { "message": "Thuật toán" @@ -407,9 +511,6 @@ "backup": { "message": "Sao lưu" }, - "backup_file_info": { - "message": "Sao chép dữ liệu thành 1 tệp tin." - }, "password_policy_default_hint": { "message": "Mật khẩu của bạn không phù hợp với yêu cầu bảo mật từ tổ chức của bạn. Liên hệ người quản lý của bạn để biết thêm thông tin." }, @@ -426,7 +527,7 @@ "message": "Việc đồng bộ hóa trình duyệt đã bị tắt. Hãy kích hoạt nó để cho phép các tài khoản được đồng bộ trên các trình duyệt." }, "advisor_insight_auto_fill_not_enabled": { - "message": "Autofill có thể được cho phép để tự động điền dữ liệu vào các website." + "message": "Tự động điền có thể được bật để tự động điền mã vào các trang web." }, "advisor_insight_smart_filter_not_enabled": { "message": "Bật bộ lọc thông minh cho phép truy cập nhanh vào tài khoản." @@ -450,34 +551,34 @@ "message": "Bỏ qua" }, "learn_more": { - "message": "Xem thêm" + "message": "Tìm hiểu thêm" }, "enable_context_menu": { - "message": "Thêm vào context menu" + "message": "Thêm vào menu ngữ cảnh" }, "no_entires": { - "message": "Không có tài khoản nào để hiển thị. Hãy thêm tài khoản của bạn." + "message": "Không có tài khoản nào để hiển thị. Hãy thêm tài khoản đầu tiên của bạn ngay." }, "permissions": { - "message": "Permissions" + "message": "Quyền" }, "permission_revoke": { - "message": "Revoke" + "message": "Thu hồi" }, "permission_show_required_permissions": { - "message": "Show non-revocable permissions" + "message": "Hiển thị các quyền không thể thu hồi" }, "permission_required": { - "message": "This is a required permission and cannot be revoked." + "message": "Đây là quyền bắt buộc và không thể thu hồi." }, "permission_active_tab": { - "message": "Access to the current tab to scan QR codes." + "message": "Truy cập tab hiện tại để quét mã QR." }, "permission_storage": { - "message": "Access to browser storage to store account data." + "message": "Truy cập bộ nhớ trình duyệt để lưu trữ dữ liệu tài khoản." }, "permission_identity": { - "message": "Allows sign in to 3rd party storage services." + "message": "Cho phép đăng nhập vào các dịch vụ lưu trữ của bên thứ ba." }, "permission_alarms": { "message": "Cho phép khóa tự động." @@ -486,39 +587,31 @@ "message": "Đưa tệp lệnh vào tab hiện tại để quét mã QR và cho phép tự động điền." }, "permission_clipboard_write": { - "message": "Grants write-only access to the clipboard to copy codes to clipboard when you click on the account." + "message": "Cấp quyền chỉ ghi vào bộ nhớ tạm để sao chép mã khi bạn nhấn vào tài khoản." }, "permission_context_menus": { - "message": "Adds Authenticator to context menu." + "message": "Thêm OTPilot vào menu ngữ cảnh." }, "permission_sync_clock": { - "message": "Allows clock sync with Google." + "message": "Cho phép đồng bộ đồng hồ với Google." }, "permission_dropbox": { - "message": "Allows backup to Dropbox." + "message": "Cho phép sao lưu lên Dropbox." }, "permission_dropbox_cannot_revoke": { - "message": "You must disable Dropbox backup first." - }, - "permission_drive": { - "message": "Allows backup to Google Drive." - }, - "permission_drive_cannot_revoke": { - "message": "You must disable Google Drive backup first." - }, - "permission_onedrive": { - "message": "Allows backup to OneDrive." - }, - "permission_onedrive_cannot_revoke": { - "message": "You must disable OneDrive backup first." + "message": "Bạn phải tắt sao lưu Dropbox trước." }, "permission_unknown_permission": { - "message": "Unknown permission. If see this message, please send a bug report." + "message": "Quyền không xác định. Nếu bạn thấy thông báo này, vui lòng gửi báo cáo lỗi." }, "phrase_wrong": { "message": "Mật khẩu sai" }, "activate_auto_filter": { - "message": "Cảnh báo: Bộ lọc thông minh không khớp với tên của tài khoản. Hãy xác nhận bạn đang ở trang web chính xác trước khi nhập mã!" + "message": "Cảnh báo: Bộ lọc thông minh chỉ khớp gần đúng tên miền với tài khoản. Luôn xác minh bạn đang ở đúng trang web trước khi nhập mã!" + }, + "backup_unavailable": { + "message": "Chưa hỗ trợ", + "description": "Shown on a cloud provider that is not implemented yet." } } diff --git a/_locales/zh_CN/messages.json b/_locales/zh_CN/messages.json index 96ee5b0ff..592647924 100644 --- a/_locales/zh_CN/messages.json +++ b/_locales/zh_CN/messages.json @@ -1,34 +1,46 @@ { "extName": { - "message": "身份验证器", + "message": "OTPilot Authenticator", "description": "Extension Name." }, "extShortName": { - "message": "身份验证器", + "message": "OTPilot", "description": "Extension Short Name." }, + "host": { + "message": "网站", + "description": "Bound website host used to restrict autofill to a matching page." + }, + "backup_requires_password": { + "message": "请先设置密码才能使用云备份,您的密钥会在离开此设备前先行加密。", + "description": "Shown in the cloud backup section when no master password is set." + }, "extDesc": { - "message": "身份验证器用以在浏览器中生成二步认证代码。", + "message": "OTPilot Authenticator 可生成两步验证 (2FA) 验证码,并提供加密云备份、按网站匹配的自动填充和安全建议。", "description": "Extension Description." }, "added": { - "message": "已添加。", + "message": " 已添加。", "description": "Added Account." }, "errorqr": { - "message": "无法识别的QR码。", + "message": "无法识别的二维码。", "description": "QR Error." }, "errorsecret": { "message": "无效的密钥", "description": "Secret Error." }, + "errorissuer": { + "message": "签发方不能包含“::”", + "description": "Issuer Error." + }, "add_code": { - "message": "添加账号", + "message": "添加账户", "description": "Add account." }, "add_qr": { - "message": "扫描QR码", + "message": "扫描二维码", "description": "Scan QR Code." }, "add_secret": { @@ -36,17 +48,13 @@ "description": "Manual Entry." }, "migration_fail": { - "message": "导入失败。 如果您要从Google Authenticator迁移数据,请从Google Authenticator重新导出数据,然后重试。", + "message": "导入失败。如果您要从 Google Authenticator 迁移数据,请从 Google Authenticator 重新导出数据,然后重试。", "description": "Import migration data failed." }, "migration_partly_fail": { - "message": "某些帐户数据未成功导入。", + "message": "某些账户数据未成功导入。", "description": "Some migration data is broken." }, - "close": { - "message": "关闭", - "description": "Close." - }, "ok": { "message": "确定", "description": "OK." @@ -99,10 +107,6 @@ "message": "当前密码", "description": "Current Passphrase." }, - "new_phrase": { - "message": "新密码", - "description": "New Passphrase." - }, "phrase": { "message": "密码", "description": "Passphrase." @@ -112,7 +116,7 @@ "description": "Confirm Passphrase." }, "confirm_delete": { - "message": "您确定要删除此密钥吗?该操作无法撤销。", + "message": "您确定要删除此账户吗?此操作无法撤销。", "description": "Remove entry confirmation" }, "confirm_delete_all": { @@ -120,25 +124,21 @@ "description": "Message that user is required to acknowledge before clearing all data." }, "delete_all": { - "message": "重置身份验证器" + "message": "重置 OTPilot" }, "delete_all_warning": { - "message": "这将删除所有数据并完全重置身份验证器。 任何已删除的数据都无法恢复!在重置身份验证器之前您应保存备份。" + "message": "这将删除所有数据并完全重置 OTPilot。任何已删除的数据都无法恢复!在重置 OTPilot 之前您应保存备份。" }, "security_warning": { - "message": "您的账户数据将使用此密码进行加密。如果您忘记了密码没有人能够提供帮助。", + "message": "您的账户数据将使用此密码进行加密。如果您忘记了密码,没有人能够提供帮助。", "description": "Passphrase Warning." }, - "update": { - "message": "更新", - "description": "Update." - }, "phrase_incorrect": { - "message": "部分账户与密码不匹配,您无法添加新账户或者更改密码。请提供正确的密码后重试。", + "message": "在所有账户解密之前,您无法添加新账户。请先输入正确的密码再继续。", "description": "Passphrase Incorrect." }, "phrase_incorrect_export": { - "message": "备份文件不会包含无法解密的账号。", + "message": "备份文件不会包含无法解密的账户。", "description": "Skip Unable-decripted Data." }, "phrase_not_match": { @@ -157,26 +157,18 @@ "message": "问题反馈", "description": "Feedback." }, - "translate": { - "message": "参与翻译", - "description": "Translate." - }, "source": { "message": "源代码", "description": "Source Code." }, "passphrase_info": { - "message": "输入密码以解码账户数据。", + "message": "输入密码以解密账户数据。", "description": "Passphrase Info" }, "sync_clock": { - "message": "通过Google校准时间", + "message": "通过 Google 校准时间", "description": "Sync Clock" }, - "remember_phrase": { - "message": "记住密码", - "description": "Remember Passphrase" - }, "clock_too_far_off": { "message": "注意!您的本地时钟时间差过大,请修正后再进行操作。", "description": "Local Time is Too Far Off" @@ -186,11 +178,11 @@ "description": "Remind Backup" }, "capture_failed": { - "message": "捕捉失败,请重载您正在浏览的页面后重试。", + "message": "捕捉失败,请重新加载页面后重试。", "description": "Capture Failed" }, "capture_local_file_failed": { - "message": "您是否正在尝试从本地文件扫描QR码? 改为使用导入QR图像备份。", + "message": "您是否正在尝试从本地文件扫描二维码?请改用「导入二维码图片备份」功能。", "description": "Import QR image backup instead of scan local image" }, "based_on_time": { @@ -202,7 +194,7 @@ "description": "Counter Based" }, "resize_popup_page": { - "message": "偏好", + "message": "偏好设置", "description": "Popup Page Settings" }, "scale": { @@ -210,7 +202,7 @@ "description": "Scale" }, "export_info": { - "message": "警告:所有备份均未加密。想要将账号添加至其他应用?请点击账号右上角隐藏的图标。", + "message": "警告:所有备份均未加密。想要将账户添加到其他应用?将鼠标悬停在任意账户右上角,点击隐藏的按钮即可。", "description": "Export menu info text" }, "download_backup": { @@ -226,27 +218,59 @@ "description": "Import backup file." }, "import_backup_qr": { - "message": "导入 QR 图片备份", + "message": "导入二维码图片备份", "description": "Import qr image backup." }, "import_qr_images": { - "message": "导入QR码图片", + "message": "导入二维码图片", "description": "Import qr images. Shown as add account method." }, "import_backup_code": { - "message": "导入备份文本", + "message": "导入 OTP 链接", "description": "Import backup code." }, "import_otp_urls": { - "message": "导入OTP链接", + "message": "导入 OTP 链接", "description": "Import OTP URLs. Shown as add account method." }, + "import_drop_file": { + "message": "拖放备份文件到此,或点击浏览", + "description": "File import dropzone title" + }, + "import_file_accepts": { + "message": "支持 .json 和 .txt 导出文件", + "description": "File import dropzone hint" + }, + "import_choose_file": { + "message": "选择文件", + "description": "File import dropzone button" + }, + "import_encrypted_note": { + "message": "若文件已加密,选择后会要求输入其密码。", + "description": "File import note about encrypted backups" + }, + "import_enc_required": { + "message": "已加密 — 需要密码", + "description": "Badge on a picked encrypted backup file" + }, + "import_pass_placeholder": { + "message": "输入文件的密码", + "description": "Passphrase input placeholder for encrypted import" + }, + "import_pass_hint": { + "message": "这是您创建备份时设置的密码。", + "description": "Hint under the import passphrase field" + }, + "import_decrypt": { + "message": "解密并导入", + "description": "Button to decrypt and import an encrypted backup" + }, "import_backup_qr_partly_failed": { "message": "导入成功,但部分二维码无法识别。", "description": "Import successful, but some QR image cannot be recognized." }, "import_backup_qr_in_batches": { - "message": "您可以同时选择多个文件进行批量导入", + "message": "您可以同时选择多个文件进行批量导入。", "description": "You can select multiple image files to import backup in batches." }, "show_all_entries": { @@ -297,14 +321,106 @@ "message": "启用自动填充", "description": "Use Autofill" }, - "use_high_contrast": { - "message": "使用高对比度", - "description": "Use High Contrast" - }, "theme": { "message": "主题", "description": "Theme" }, + "settings_appearance": { + "message": "外观", + "description": "Settings section: appearance" + }, + "settings_general": { + "message": "常规", + "description": "Settings section: general" + }, + "edit_accounts": { + "message": "编辑账户", + "description": "Header title while in edit mode" + }, + "show_qr": { + "message": "显示二维码", + "description": "Context-menu action to show an account's QR code" + }, + "pin_to_top": { + "message": "置顶", + "description": "Context-menu action to pin an account to the top" + }, + "unpin": { + "message": "取消置顶", + "description": "Context-menu action to unpin an account" + }, + "vault_locked": { + "message": "保险库已锁定", + "description": "Title on the unlock screen" + }, + "unlock": { + "message": "解锁", + "description": "Unlock button" + }, + "qr_transfer_title": { + "message": "转移此账户", + "description": "QR sheet caption title" + }, + "qr_transfer_desc": { + "message": "在另一台设备上用 OTPilot 扫描此二维码即可转移此账户 — 数据绝不离开您的设备。", + "description": "QR sheet caption body" + }, + "backup_on_device": { + "message": "本机", + "description": "Backup section: local" + }, + "backup_cloud_sync": { + "message": "云同步", + "description": "Backup section: cloud" + }, + "backup_connected": { + "message": "已连接", + "description": "Cloud provider connected status" + }, + "backup_not_connected": { + "message": "未连接", + "description": "Cloud provider not-connected status" + }, + "showing_codes_for": { + "message": "正在显示以下网站的验证码:", + "description": "Smart-filter banner prefix, followed by the site domain" + }, + "other_accounts": { + "message": "其他账户", + "description": "Divider above non-matching accounts in the filter view" + }, + "filter_to_site": { + "message": "筛选至", + "description": "Banner prefix to re-apply the site filter, followed by the domain" + }, + "onboarding_title": { + "message": "随身携带的验证码", + "description": "Onboarding headline" + }, + "onboarding_subtitle": { + "message": "为每个账户生成安全的两步验证码 — 加密、离线,且始终一键复制。", + "description": "Onboarding subtitle" + }, + "onboarding_feature_offline": { + "message": "完全离线运行 — 数据不会离开您的设备", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_encrypted": { + "message": "加密保险库,可选密码锁", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_scan": { + "message": "扫描二维码或导入现有备份", + "description": "Onboarding feature bullet" + }, + "onboarding_get_started": { + "message": "开始使用", + "description": "Onboarding primary button" + }, + "onboarding_import": { + "message": "我有备份要导入", + "description": "Onboarding secondary button" + }, "theme_light": { "message": "浅色", "description": "Light theme" @@ -313,9 +429,9 @@ "message": "深色", "description": "Dark theme" }, - "theme_simple": { - "message": "简约", - "description": "Simple theme" + "theme_auto": { + "message": "自动", + "description": "Follow the system light/dark setting" }, "theme_compact": { "message": "精简视图", @@ -325,14 +441,6 @@ "message": "高对比度", "description": "High Contrast theme" }, - "theme_flat": { - "message": "扁平", - "description": "Flat theme" - }, - "storage_sync_info": { - "message": "自动备份您的数据至第三方服务。", - "description": "3rd party backup info" - }, "browser_sync": { "message": "浏览器数据同步", "description": "Storage location" @@ -346,14 +454,14 @@ "description": "Sign in to 3rd party storage services" }, "onedrive_business_perms": { - "message": "为何企业版账户需要更高权限?" + "message": "为何企业版账户需要更多权限?" }, "log_out": { "message": "注销", "description": "Sign out of 3rd party storage services" }, "token_revoked": { - "message": "连接到您的 $SERVICE$ 账号时出错,请重试。", + "message": "连接到您的 $SERVICE$ 账号时出错,请重新登录。", "description": "Error authenticating to backup service. $SERVICE$ will be replaced with a proper noun (E.g.: 'Google Drive' or 'Dropbox')", "placeholders": { "service": { @@ -362,12 +470,8 @@ } } }, - "otp_unsupported_warn": { - "message": "您添加了Steam或战网账号,所以备份文件将不会使用标准输出格式。", - "description": "Warning if using account that is not supported by standard backup format." - }, "otp_backup_inform": { - "message": "您可以从一些其它的应用中导入备份。", + "message": "您可以从一些其他应用导入备份。", "description": "Info text on import page" }, "otp_backup_learn": { @@ -375,7 +479,7 @@ "description": "learn more link on import page. Placed after otp_backup_inform" }, "loading": { - "message": "正在载入..." + "message": "加载中..." }, "autolock": { "message": "在" @@ -393,7 +497,7 @@ "message": "类型" }, "invalid": { - "message": "无效代码" + "message": "无效" }, "digits": { "message": "位数" @@ -407,14 +511,11 @@ "backup": { "message": "备份" }, - "backup_file_info": { - "message": "备份数据到文件。" - }, "password_policy_default_hint": { "message": "您的密码不符合您组织的安全要求。有关详细信息,请联系组织管理员。" }, "advisor": { - "message": "建议" + "message": "安全建议" }, "advisor_insight_password_not_set": { "message": "设置密码以保护您的数据。" @@ -423,19 +524,19 @@ "message": "启用自动锁定来保护您的数据。" }, "advisor_insight_browser_sync_not_enabled": { - "message": "浏览器同步已被禁用。启用它以允许跨浏览器的帐户同步。" + "message": "浏览器同步已被禁用。启用它以允许跨浏览器的账户同步。" }, "advisor_insight_auto_fill_not_enabled": { - "message": "可以启用自动填充以自动将代码填充到网站中。" + "message": "启用自动填充,即可将验证码自动填入网站。" }, "advisor_insight_smart_filter_not_enabled": { "message": "开启智能过滤以便快速访问账户。" }, "show_all_insights": { - "message": "显示所有见解。" + "message": "显示所有建议。" }, "no_insight_available": { - "message": "没有可供参考的见解,一切看起来都很好!" + "message": "暂无可参考的建议,一切看起来都很好!" }, "danger": { "message": "危险" @@ -474,7 +575,7 @@ "message": "访问当前标签页以扫描二维码。" }, "permission_storage": { - "message": "访问浏览器存储以存储帐户数据。" + "message": "访问浏览器存储以存储账户数据。" }, "permission_identity": { "message": "用以登录第三方存储服务。" @@ -486,39 +587,31 @@ "message": "将脚本注入当前标签页以扫描二维码并允许自动填充功能生效。" }, "permission_clipboard_write": { - "message": "剪贴板只写权限以当您点击该帐户时复制代码到剪贴板。" + "message": "授予剪贴板只写权限,以便在您点击账户时将验证码复制到剪贴板。" }, "permission_context_menus": { - "message": "将身份验证器添加到右键菜单。" + "message": "将 OTPilot 添加到右键菜单。" }, "permission_sync_clock": { - "message": "允许从 Google 同步时间" + "message": "允许从 Google 同步时间。" }, "permission_dropbox": { - "message": "允许备份到 Dropbox" + "message": "允许备份到 Dropbox。" }, "permission_dropbox_cannot_revoke": { "message": "您必须先停用 Dropbox 备份。" }, - "permission_drive": { - "message": "允许备份到 Google Drive。" - }, - "permission_drive_cannot_revoke": { - "message": "您必须先停用 Google Drive 备份。" - }, - "permission_onedrive": { - "message": "允许备份到 OneDrive。" - }, - "permission_onedrive_cannot_revoke": { - "message": "您必须先停用 OneDrive 备份。" - }, "permission_unknown_permission": { "message": "未知权限。如果您看到此消息,请发送错误报告。" }, "phrase_wrong": { - "message": "Password incorrect" + "message": "密码错误" }, "activate_auto_filter": { - "message": "Warning: Smart filter loosely matches the domain name to an account. Always verify that you are on the correct website before entering a code!" + "message": "警告:智能过滤器是通过粗略匹配域名来关联账户的。在输入验证码前,请务必确认您所在的网站正确无误!" + }, + "backup_unavailable": { + "message": "暂不支持", + "description": "Shown on a cloud provider that is not implemented yet." } } diff --git a/_locales/zh_TW/messages.json b/_locales/zh_TW/messages.json index 07d1f3791..a367f9c06 100644 --- a/_locales/zh_TW/messages.json +++ b/_locales/zh_TW/messages.json @@ -1,34 +1,46 @@ { "extName": { - "message": "Authenticator", + "message": "OTPilot Authenticator", "description": "Extension Name." }, "extShortName": { - "message": "Authenticator", + "message": "OTPilot", "description": "Extension Short Name." }, + "host": { + "message": "網站", + "description": "Bound website host used to restrict autofill to a matching page." + }, + "backup_requires_password": { + "message": "請先設定密碼才能使用雲端備份,您的密鑰會在離開此裝置前先行加密。", + "description": "Shown in the cloud backup section when no master password is set." + }, "extDesc": { - "message": "Authenticator 在你的瀏覽器中生成兩步驟驗證碼。", + "message": "OTPilot 在您的瀏覽器中產生兩步驟驗證碼。", "description": "Extension Description." }, "added": { - "message": "已新增。", + "message": " 已新增。", "description": "Added Account." }, "errorqr": { - "message": "無法識別的QR碼。", + "message": "無法識別的 QR Code。", "description": "QR Error." }, "errorsecret": { "message": "帳號金鑰無效", "description": "Secret Error." }, + "errorissuer": { + "message": "發行者不可包含「::」", + "description": "Issuer Error." + }, "add_code": { "message": "新增帳號", "description": "Add account." }, "add_qr": { - "message": "掃描QR碼", + "message": "掃描 QR Code", "description": "Scan QR Code." }, "add_secret": { @@ -36,17 +48,13 @@ "description": "Manual Entry." }, "migration_fail": { - "message": "匯入失敗。若您要從Google Authenticator搬遷資料,請重新從Google Autenticator匯出資料後再重試", + "message": "匯入失敗。若您要從 Google Authenticator 搬移資料,請重新從 Google Authenticator 匯出資料後再試一次。", "description": "Import migration data failed." }, "migration_partly_fail": { - "message": "部分帳號資料未被成功匯入", + "message": "部分帳號資料未成功匯入。", "description": "Some migration data is broken." }, - "close": { - "message": "關閉", - "description": "Close." - }, "ok": { "message": "確定", "description": "OK." @@ -60,15 +68,15 @@ "description": "No." }, "account": { - "message": "帳戶", + "message": "帳號", "description": "Account." }, "accountName": { - "message": "用戶名", + "message": "使用者名稱", "description": "Account Name." }, "issuer": { - "message": "簽發方", + "message": "發行者", "description": "Issuer." }, "secret": { @@ -96,13 +104,9 @@ "description": "Security." }, "current_phrase": { - "message": "當前密碼", + "message": "目前密碼", "description": "Current Passphrase." }, - "new_phrase": { - "message": "新密碼", - "description": "New Passphrase." - }, "phrase": { "message": "密碼", "description": "Passphrase." @@ -112,7 +116,7 @@ "description": "Confirm Passphrase." }, "confirm_delete": { - "message": "您確定要刪除此帳戶嗎?此操作無法復原。", + "message": "您確定要刪除此帳號嗎?此操作無法復原。", "description": "Remove entry confirmation" }, "confirm_delete_all": { @@ -120,29 +124,25 @@ "description": "Message that user is required to acknowledge before clearing all data." }, "delete_all": { - "message": "重設 Authenticator" + "message": "重設 OTPilot" }, "delete_all_warning": { - "message": "這會刪除您所有的資料並重設Authenticator。您將無法復原任何已刪除的資料!在重設Authenticator之前您應備份您的資料。" + "message": "這會刪除您所有的資料並重設 OTPilot。您將無法復原任何已刪除的資料!在重設 OTPilot 之前,建議您先備份資料。" }, "security_warning": { - "message": "您的金鑰將使用此密碼進行加密。請妥善保管,如果遺失您將失去對此金鑰的權限。", + "message": "此密碼將用於加密您的帳號。如果忘記密碼,將沒有人能夠幫助您。", "description": "Passphrase Warning." }, - "update": { - "message": "更新", - "description": "Update." - }, "phrase_incorrect": { - "message": "部份帳號輸入密碼錯誤,您無法新增帳號。請在輸入正確的密碼後繼續。", + "message": "在所有帳號解密之前,您無法新增帳號。請先輸入正確的密碼再繼續。", "description": "Passphrase Incorrect." }, "phrase_incorrect_export": { - "message": "本次備份不包含無法解密的帳號", + "message": "本次備份將不包含無法解密的帳號。", "description": "Skip Unable-decripted Data." }, "phrase_not_match": { - "message": "密碼錯誤。", + "message": "密碼不相符。", "description": "Passphrase Not Match." }, "encrypted": { @@ -154,35 +154,27 @@ "description": "Copied." }, "feedback": { - "message": "問題反饋", + "message": "意見回饋", "description": "Feedback." }, - "translate": { - "message": "參與翻譯", - "description": "Translate." - }, "source": { "message": "原始碼", "description": "Source Code." }, "passphrase_info": { - "message": "輸入密碼以解碼帳戶資料。", + "message": "輸入密碼以解密帳號資料。", "description": "Passphrase Info" }, "sync_clock": { - "message": "通過Google校準時間", + "message": "透過 Google 校準時間", "description": "Sync Clock" }, - "remember_phrase": { - "message": "記住密碼", - "description": "Remember Passphrase" - }, "clock_too_far_off": { - "message": "注意!您的本地時鐘時間差異過大,請於效準時間後再進行操作。", + "message": "注意!您本機的時鐘時間誤差過大,請先校正後再繼續操作。", "description": "Local Time is Too Far Off" }, "remind_backup": { - "message": "您是否為金鑰建立了備份?別等到為時已晚才建立!", + "message": "您是否為帳號建立了備份?別等到為時已晚才建立!", "description": "Remind Backup" }, "capture_failed": { @@ -190,15 +182,15 @@ "description": "Capture Failed" }, "capture_local_file_failed": { - "message": "您是否試著從本地檔案掃描QR碼? 請改用 匯入QR碼圖片備份。", + "message": "您是否想從本機檔案掃描 QR Code?請改用「匯入 QR Code 圖片備份」。", "description": "Import QR image backup instead of scan local image" }, "based_on_time": { - "message": "驗證碼", + "message": "TOTP", "description": "Time Based" }, "based_on_counter": { - "message": "一次性驗證碼", + "message": "HOTP", "description": "Counter Based" }, "resize_popup_page": { @@ -210,7 +202,7 @@ "description": "Scale" }, "export_info": { - "message": "警告:所有備份均未加密。想要將賬號新增至其他應用?請點選賬號右上角隱藏的圖示。", + "message": "警告:所有備份均未加密。想將帳號新增至其他應用程式嗎?將游標移到任一帳號的右上角,並點選隱藏的按鈕。", "description": "Export menu info text" }, "download_backup": { @@ -222,35 +214,67 @@ "description": "Import backup." }, "import_backup_file": { - "message": "以檔案匯入", + "message": "匯入檔案", "description": "Import backup file." }, "import_backup_qr": { - "message": "导入 QR 图片备份", + "message": "匯入 QR Code 圖片", "description": "Import qr image backup." }, "import_qr_images": { - "message": "匯入 QR 圖片", + "message": "匯入 QR Code 圖片", "description": "Import qr images. Shown as add account method." }, "import_backup_code": { - "message": "以文字匯入", + "message": "匯入 OTPAuth URI", "description": "Import backup code." }, "import_otp_urls": { - "message": "匯入 OTP 網址", + "message": "匯入 OTPAuth URI", "description": "Import OTP URLs. Shown as add account method." }, + "import_drop_file": { + "message": "拖曳備份檔到此,或點擊瀏覽", + "description": "File import dropzone title" + }, + "import_file_accepts": { + "message": "支援 .json 與 .txt 匯出檔", + "description": "File import dropzone hint" + }, + "import_choose_file": { + "message": "選擇檔案", + "description": "File import dropzone button" + }, + "import_encrypted_note": { + "message": "若檔案已加密,選取後將會要求輸入密碼。", + "description": "File import note about encrypted backups" + }, + "import_enc_required": { + "message": "已加密——需要密碼", + "description": "Badge on a picked encrypted backup file" + }, + "import_pass_placeholder": { + "message": "輸入此檔案的密碼", + "description": "Passphrase input placeholder for encrypted import" + }, + "import_pass_hint": { + "message": "這是你建立備份時設定的密碼。", + "description": "Hint under the import passphrase field" + }, + "import_decrypt": { + "message": "解密並匯入", + "description": "Button to decrypt and import an encrypted backup" + }, "import_backup_qr_partly_failed": { - "message": "匯入成功,但部份QR碼無法辨識", + "message": "匯入成功,但部分 QR Code 無法辨識。", "description": "Import successful, but some QR image cannot be recognized." }, "import_backup_qr_in_batches": { - "message": "您可以選擇多個檔案進行批量匯入", + "message": "您可以選擇多個檔案以批次匯入備份。", "description": "You can select multiple image files to import backup in batches." }, "show_all_entries": { - "message": "顯示全部條目", + "message": "顯示所有項目", "description": "Show all entries." }, "dropbox_risk": { @@ -262,7 +286,7 @@ "description": "Error password warning when import backups." }, "local_passphrase_warning": { - "message": "您的密碼儲存在了本地,請立即通過安全選單更改密碼。", + "message": "您的密碼儲存在本機,請立即透過安全選單變更密碼。", "description": "localStorage password warning." }, "remove": { @@ -270,7 +294,7 @@ "description": "Remove password." }, "download_enc_backup": { - "message": "下載密碼保護備份", + "message": "下載受密碼保護的備份", "description": "Download Encrypted Backup" }, "search": { @@ -294,17 +318,109 @@ "description": "Manual backup" }, "use_autofill": { - "message": "啟用自動填充", + "message": "啟用自動填入", "description": "Use Autofill" }, - "use_high_contrast": { - "message": "高對比", - "description": "Use High Contrast" - }, "theme": { "message": "主題", "description": "Theme" }, + "settings_appearance": { + "message": "外觀", + "description": "Settings section: appearance" + }, + "settings_general": { + "message": "一般", + "description": "Settings section: general" + }, + "edit_accounts": { + "message": "編輯帳號", + "description": "Header title while in edit mode" + }, + "show_qr": { + "message": "顯示 QR Code", + "description": "Context-menu action to show an account's QR code" + }, + "pin_to_top": { + "message": "釘選到最上方", + "description": "Context-menu action to pin an account to the top" + }, + "unpin": { + "message": "取消釘選", + "description": "Context-menu action to unpin an account" + }, + "vault_locked": { + "message": "資料已鎖定", + "description": "Title on the unlock screen" + }, + "unlock": { + "message": "解鎖", + "description": "Unlock button" + }, + "qr_transfer_title": { + "message": "轉移此帳號", + "description": "QR sheet caption title" + }, + "qr_transfer_desc": { + "message": "在另一台裝置上用 OTPilot 掃描此碼即可轉移此帳號——資料不會離開你的裝置。", + "description": "QR sheet caption body" + }, + "backup_on_device": { + "message": "本機裝置", + "description": "Backup section: local" + }, + "backup_cloud_sync": { + "message": "雲端同步", + "description": "Backup section: cloud" + }, + "backup_connected": { + "message": "已連線", + "description": "Cloud provider connected status" + }, + "backup_not_connected": { + "message": "未連線", + "description": "Cloud provider not-connected status" + }, + "showing_codes_for": { + "message": "顯示以下網站的驗證碼:", + "description": "Smart-filter banner prefix, followed by the site domain" + }, + "other_accounts": { + "message": "其他帳號", + "description": "Divider above non-matching accounts in the filter view" + }, + "filter_to_site": { + "message": "只顯示此網站:", + "description": "Banner prefix to re-apply the site filter, followed by the domain" + }, + "onboarding_title": { + "message": "隨身攜帶的驗證碼", + "description": "Onboarding headline" + }, + "onboarding_subtitle": { + "message": "為每個帳號產生安全的兩步驟驗證碼——加密、離線,隨時一鍵複製。", + "description": "Onboarding subtitle" + }, + "onboarding_feature_offline": { + "message": "完全離線運作——資料不離開你的裝置", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_encrypted": { + "message": "加密資料,可選用密碼鎖", + "description": "Onboarding feature bullet" + }, + "onboarding_feature_scan": { + "message": "掃描 QR Code 或匯入既有備份", + "description": "Onboarding feature bullet" + }, + "onboarding_get_started": { + "message": "開始使用", + "description": "Onboarding primary button" + }, + "onboarding_import": { + "message": "我有備份要匯入", + "description": "Onboarding secondary button" + }, "theme_light": { "message": "亮色", "description": "Light theme" @@ -313,26 +429,18 @@ "message": "暗色", "description": "Dark theme" }, - "theme_simple": { - "message": "簡易", - "description": "Simple theme" + "theme_auto": { + "message": "自動(跟隨系統)", + "description": "Follow the system light/dark setting" }, "theme_compact": { - "message": "壓縮", + "message": "緊湊", "description": "Compact theme" }, "theme_high_contrast": { "message": "高對比度", "description": "High Contrast theme" }, - "theme_flat": { - "message": "平", - "description": "Flat theme" - }, - "storage_sync_info": { - "message": "自動備份您的資料至第三方儲存服務。", - "description": "3rd party backup info" - }, "browser_sync": { "message": "瀏覽器同步", "description": "Storage location" @@ -346,14 +454,14 @@ "description": "Sign in to 3rd party storage services" }, "onedrive_business_perms": { - "message": "為什麼企業賬戶需要更多權限?" + "message": "為什麼企業帳號需要更多權限?" }, "log_out": { "message": "登出", "description": "Sign out of 3rd party storage services" }, "token_revoked": { - "message": "連接到您的 $SERVICE$ 帳戶時出現問題,請重試。", + "message": "連線到您的 $SERVICE$ 帳號時發生問題,請重新登入。", "description": "Error authenticating to backup service. $SERVICE$ will be replaced with a proper noun (E.g.: 'Google Drive' or 'Dropbox')", "placeholders": { "service": { @@ -362,16 +470,12 @@ } } }, - "otp_unsupported_warn": { - "message": "您有一個或多個Steam或Blizzard帳戶。 未加密的備份將不使用標準化備份格式。", - "description": "Warning if using account that is not supported by standard backup format." - }, "otp_backup_inform": { - "message": "您可以從其他一些應用程序導入備份。", + "message": "您可以從其他一些應用程式匯入備份。", "description": "Info text on import page" }, "otp_backup_learn": { - "message": "瞭解詳情", + "message": "了解更多", "description": "learn more link on import page. Placed after otp_backup_inform" }, "loading": { @@ -387,7 +491,7 @@ "message": "進階設定" }, "period": { - "message": "有效期" + "message": "週期" }, "type": { "message": "類型" @@ -396,10 +500,10 @@ "message": "無效" }, "digits": { - "message": "數字" + "message": "位數" }, "algorithm": { - "message": "算法" + "message": "演算法" }, "smart_filter": { "message": "智慧過濾" @@ -407,35 +511,32 @@ "backup": { "message": "備份" }, - "backup_file_info": { - "message": "備份至檔案" - }, "password_policy_default_hint": { - "message": "您的密碼不符合您組織的安全要求。 有關詳細信息,請聯繫您的管理員。" + "message": "您的密碼不符合貴組織的安全要求。如需更多資訊,請聯絡您的系統管理員。" }, "advisor": { "message": "顧問" }, "advisor_insight_password_not_set": { - "message": "設定密碼保護您的資料。" + "message": "設定密碼以保護您的資料。" }, "advisor_insight_auto_lock_not_set": { - "message": "啟用自動鎖定以保護你的資料" + "message": "啟用自動鎖定以保護您的資料。" }, "advisor_insight_browser_sync_not_enabled": { - "message": "已禁用瀏覽器同步。啟用以在不同瀏覽器同步賬號。" + "message": "已停用瀏覽器同步。啟用後即可在不同瀏覽器間同步帳號。" }, "advisor_insight_auto_fill_not_enabled": { - "message": "可以啟動自動填入以自動填入代碼到網站中" + "message": "可啟用自動填入,自動將驗證碼填入網站。" }, "advisor_insight_smart_filter_not_enabled": { - "message": "啟用智能過濾器可以快速訪問帳戶。" + "message": "啟用智慧過濾可快速存取帳號。" }, "show_all_insights": { "message": "顯示所有見解。" }, "no_insight_available": { - "message": "無任何見解被發現;一切完好!" + "message": "未發現任何見解,一切看起來都很好!" }, "danger": { "message": "危險" @@ -447,78 +548,70 @@ "message": "資訊" }, "dismiss": { - "message": "解除" + "message": "忽略" }, "learn_more": { "message": "了解更多" }, "enable_context_menu": { - "message": "加到右鍵選單" + "message": "加入右鍵選單" }, "no_entires": { - "message": "帳號不存在,請新增帳號。" + "message": "沒有可顯示的帳號,立即新增您的第一個帳號。" }, "permissions": { "message": "使用權限" }, "permission_revoke": { - "message": "註銷" + "message": "撤銷" }, "permission_show_required_permissions": { - "message": "顯示不可註銷的權限。" + "message": "顯示不可撤銷的權限。" }, "permission_required": { - "message": "這是必要的權限而且不可以註銷。" + "message": "這是必要的權限,無法撤銷。" }, "permission_active_tab": { - "message": "存取當前頁面並掃描QR碼" + "message": "存取目前分頁以掃描 QR Code。" }, "permission_storage": { - "message": "存取瀏覽器空間以便存放帳戶資訊" + "message": "存取瀏覽器儲存空間以存放帳號資料。" }, "permission_identity": { "message": "允許登入第三方儲存服務。" }, "permission_alarms": { - "message": "Allows auto-lock to work." + "message": "允許自動鎖定運作。" }, "permission_scripting": { - "message": "Inject scripts into he current tab to scan QR codes and allow auto-fill to work." + "message": "在目前分頁注入指令碼以掃描 QR Code,並讓自動填入功能運作。" }, "permission_clipboard_write": { - "message": "授權點選帳戶時複製驗證碼到剪貼簿。" + "message": "授權在您點選帳號時將驗證碼複製到剪貼簿。" }, "permission_context_menus": { - "message": "將驗證器加到右鍵選單" + "message": "將 OTPilot 加入右鍵選單。" }, "permission_sync_clock": { "message": "允許與 Google 連線校時。" }, "permission_dropbox": { - "message": "允許備份到Dropbox." + "message": "允許備份到 Dropbox。" }, "permission_dropbox_cannot_revoke": { - "message": "請先取消 Dropbox 備份" - }, - "permission_drive": { - "message": "允許備份到Google Drive." - }, - "permission_drive_cannot_revoke": { - "message": "請先取消 Google Drive 備份" - }, - "permission_onedrive": { - "message": "允許備份到OneDrive." - }, - "permission_onedrive_cannot_revoke": { - "message": "請先取消 OneDrvie 備份" + "message": "請先停用 Dropbox 備份。" }, "permission_unknown_permission": { - "message": "未知的權限。您如果看到這個訊息,請回報程式異常。" + "message": "未知的權限。如果您看到此訊息,請回報程式錯誤。" }, "phrase_wrong": { - "message": "Password incorrect" + "message": "密碼不正確" }, "activate_auto_filter": { - "message": "Warning: Smart filter loosely matches the domain name to an account. Always verify that you are on the correct website before entering a code!" + "message": "警告:智慧過濾會以較寬鬆的方式比對網域名稱與帳號。輸入驗證碼前,請務必確認您位於正確的網站!" + }, + "backup_unavailable": { + "message": "暫不支援", + "description": "Shown on a cloud provider that is not implemented yet." } } diff --git a/crowdin.yml b/crowdin.yml deleted file mode 100644 index 987c85f93..000000000 --- a/crowdin.yml +++ /dev/null @@ -1,8 +0,0 @@ -files: - - source: /_locales/en/messages.json - translation: /_locales/%two_letters_code%/messages.json - languages_mapping: - two_letters_code: - zh-CN: zh_CN - zh-TW: zh_TW - pt-BR: pt_BR diff --git a/images/icon.svg b/images/icon.svg index 6678b02b0..144e7737b 100644 --- a/images/icon.svg +++ b/images/icon.svg @@ -1,24 +1,8 @@ - - - - - - - - - - - - - - - - - - - - - - + + + + + 076 + 721 diff --git a/images/icon128.png b/images/icon128.png index b96b52c57..8c3df5156 100644 Binary files a/images/icon128.png and b/images/icon128.png differ diff --git a/images/icon16.png b/images/icon16.png index 2e5104503..d68669dcc 100644 Binary files a/images/icon16.png and b/images/icon16.png differ diff --git a/images/icon19.png b/images/icon19.png index 1e1823a5b..d82c9028f 100644 Binary files a/images/icon19.png and b/images/icon19.png differ diff --git a/images/icon38.png b/images/icon38.png index 079ee2936..92e5c6797 100644 Binary files a/images/icon38.png and b/images/icon38.png differ diff --git a/images/icon48.png b/images/icon48.png index d6fedbad6..34b593ec5 100644 Binary files a/images/icon48.png and b/images/icon48.png differ diff --git a/manifests/manifest-chrome-testing.json b/manifests/manifest-chrome-testing.json index 96d128949..18d2e3972 100644 --- a/manifests/manifest-chrome-testing.json +++ b/manifests/manifest-chrome-testing.json @@ -41,7 +41,7 @@ ] }, "background": { - "service_worker": "dist/background.js" + "service_worker": "js/background.js" }, "sandbox": { "pages": [ diff --git a/manifests/manifest-chrome.json b/manifests/manifest-chrome.json index e792d9c3b..6a16d64b4 100644 --- a/manifests/manifest-chrome.json +++ b/manifests/manifest-chrome.json @@ -1,8 +1,9 @@ { "manifest_version": 3, + "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv1vBiM9sm3mAj4/i0+XIgOVhVedsypV2XVpGW864RwWPuaOnVi0xJ3rhg4jxulOwA0CS/zoO5rxeK+yhT9PByZ9YlGtJGR7Cg6tPHlIXf37Oy5w6dCXCuqjhVAaI0Ka+oXA5XPWd07Em0v2xlqEZdHTu95XDy7oOexDKbSJEXzg6BsdymIzn/tw1LG7+iWUOYfm+7sTIOTYQPFvFgPV5AuVPSOeQNDloT89jxYpmJFD7LMkrd6gQx+MRB9J93kmG8LY2thfLpgQh3kbeuObGD0Zh1xoeCoMPOstrDYTK8kHRhB/CFSb3XAn1+wrh3CUnr+Rc/m7eGb85OsYArJyFoQIDAQAB", "name": "__MSG_extName__", "short_name": "__MSG_extShortName__", - "version": "8.0.2", + "version": "8.0.3", "default_locale": "en", "description": "__MSG_extDesc__", "icons": { @@ -34,14 +35,8 @@ "storage": { "managed_schema": "schema.json" }, - "oauth2": { - "client_id": "292457304165-u8ve4j79gag5o231n5u2pdtdrbfdo1hh.apps.googleusercontent.com", - "scopes": [ - "https://www.googleapis.com/auth/drive.file" - ] - }, "background": { - "service_worker": "dist/background.js" + "service_worker": "js/background.js" }, "sandbox": { "pages": [ @@ -61,14 +56,10 @@ ], "optional_host_permissions": [ "https://www.google.com/", - "https://*.dropboxapi.com/*", - "https://www.googleapis.com/*", - "https://accounts.google.com/o/oauth2/revoke", - "https://graph.microsoft.com/me/*", - "https://login.microsoftonline.com/common/oauth2/v2.0/token" + "https://*.dropboxapi.com/*" ], "content_security_policy": { - "extension_pages": "script-src 'self'; font-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; connect-src https://www.google.com/ https://*.dropboxapi.com https://www.googleapis.com/ https://accounts.google.com/o/oauth2/revoke https://login.microsoftonline.com/common/oauth2/v2.0/token https://graph.microsoft.com/; default-src 'none'", + "extension_pages": "script-src 'self'; font-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; connect-src https://www.google.com/ https://*.dropboxapi.com; default-src 'none'", "sandbox": "sandbox allow-scripts; script-src 'self' 'unsafe-eval';" } } diff --git a/manifests/manifest-edge.json b/manifests/manifest-edge.json index 9997b05a4..2bea7791e 100644 --- a/manifests/manifest-edge.json +++ b/manifests/manifest-edge.json @@ -40,7 +40,7 @@ ] }, "background": { - "service_worker": "dist/background.js" + "service_worker": "js/background.js" }, "sandbox": { "pages": [ diff --git a/manifests/manifest-firefox-testing.json b/manifests/manifest-firefox-testing.json index 7cdf5c7bc..b4c0d8830 100644 --- a/manifests/manifest-firefox-testing.json +++ b/manifests/manifest-firefox-testing.json @@ -27,7 +27,7 @@ }, "background": { "scripts": [ - "dist/background.js" + "js/background.js" ] }, "commands": { diff --git a/manifests/manifest-firefox.json b/manifests/manifest-firefox.json index 0a5717b49..8d82b9ad2 100644 --- a/manifests/manifest-firefox.json +++ b/manifests/manifest-firefox.json @@ -25,7 +25,7 @@ "default_popup": "view/popup.html" }, "background": { - "scripts": ["dist/background.js"] + "scripts": ["js/background.js"] }, "commands": { "_execute_action": {}, diff --git a/package-lock.json b/package-lock.json index 1bb8b48a7..fc0def058 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,32 +9,33 @@ "version": "0.1.0", "license": "MIT", "dependencies": { - "@types/lodash": "^4.14.166", "argon2-browser": "^1.18.0", + "crypto-js": "^4.1.1", "jsqr": "^1.3.1", "node-gost-crypto": "^1.0.2", "qrcode-generator": "^1.4.4", "qrcode-reader": "^1.0.4", - "vue": "^2.7.16", - "vue2-dragula": "^2.5.4", - "vuex": "^3.4.0" + "vue": "^3.4.21", + "vuedraggable": "^4.1.0", + "vuex": "^4.1.0" }, "devDependencies": { "@types/argon2-browser": "^1.18.1", "@types/chai": "^4.2.14", "@types/chrome": "^0.0.266", "@types/crypto-js": "^4.1.1", + "@types/lodash": "^4.14.166", "@types/mocha": "^10.0.6", "@types/sinon": "^17.0.2", "@types/sinon-chai": "^3.2.12", "@types/sinon-chrome": "^2.2.10", "@typescript-eslint/eslint-plugin": "^7.15.0", "@typescript-eslint/parser": "^7.15.0", - "@vue/test-utils": "^1.1.1", + "@vue/compiler-sfc": "^3.4.21", + "@vue/test-utils": "^2.4.5", "base64-loader": "^1.0.0", "buffer": "^6.0.3", "chai": "^4.2.0", - "crypto-js": "^4.1.1", "eslint": "^8.56.0", "fork-ts-checker-webpack-plugin": "^6.5.3", "lodash": "^4.17.21", @@ -53,9 +54,8 @@ "typescript": "^5.0.0", "url-loader": "^4.0.0", "util": "^0.12.5", - "vue-loader": "^15.10.1", - "vue-svg-loader": "^0.16.0", - "vue-template-compiler": "^2.7.16", + "vue-loader": "^17.4.2", + "vue-svg-loader": "0.17.0-beta.2", "webpack": "^5.94.0", "webpack-cli": "^5.0.0", "webpack-merge": "^5.0.0" @@ -282,19 +282,19 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", - "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", - "dev": true, + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", - "dev": true, + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -337,9 +337,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.6.tgz", - "integrity": "sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, "bin": { "parser": "bin/babel-parser.js" }, @@ -392,14 +396,13 @@ } }, "node_modules/@babel/types": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.6.tgz", - "integrity": "sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==", - "dev": true, + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", + "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.23.4", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -773,10 +776,10 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.25", @@ -823,6 +826,13 @@ "node": ">= 8" } }, + "node_modules/@one-ini/wasm": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz", + "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==", + "dev": true, + "license": "MIT" + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -1040,7 +1050,8 @@ "node_modules/@types/lodash": { "version": "4.14.192", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.192.tgz", - "integrity": "sha512-km+Vyn3BYm5ytMO13k9KTp27O75rbQ0NFw+U//g+PX7VZyjCioXaRFisqSIJRECljcTv73G3i6BpglNGHgUQ5A==" + "integrity": "sha512-km+Vyn3BYm5ytMO13k9KTp27O75rbQ0NFw+U//g+PX7VZyjCioXaRFisqSIJRECljcTv73G3i6BpglNGHgUQ5A==", + "dev": true }, "node_modules/@types/mocha": { "version": "10.0.6", @@ -1061,10 +1072,11 @@ "dev": true }, "node_modules/@types/q": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.5.tgz", - "integrity": "sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ==", - "dev": true + "version": "1.5.8", + "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.8.tgz", + "integrity": "sha512-hroOstUScF6zhIi+5+x0dzqrHA1EJi+Irri6b1fxolMTqqHIV/Cg77EtnQcZqZCu8hR3mX2BzIxN4/GzI68Kfw==", + "dev": true, + "license": "MIT" }, "node_modules/@types/sinon": { "version": "17.0.2", @@ -1326,90 +1338,131 @@ "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", "dev": true }, + "node_modules/@vue/compiler-core": { + "version": "3.5.38", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.38.tgz", + "integrity": "sha512-s99aGxWYig9ErHbct27KXEGhrBYlRI6c4MwAgXErOAbX9xiW37/uMa+XUDO69zLz83dng8UUZ70CTOJrLrYrEQ==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@vue/shared": "3.5.38", + "entities": "^7.0.1", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.38", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.38.tgz", + "integrity": "sha512-JTqp25l8aFfJYF7/KmsXZjAxJz7T+SjmTJLoXVjHtc2BrSgSiW2n9Aem/cWq1OPe68A8JL06B3eVdhlP0H4TVw==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.38", + "@vue/shared": "3.5.38" + } + }, "node_modules/@vue/compiler-sfc": { - "version": "2.7.16", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-2.7.16.tgz", - "integrity": "sha512-KWhJ9k5nXuNtygPU7+t1rX6baZeqOYLEforUPjgNDBnLicfHCoi48H87Q8XyLZOrNNsmhuwKqtpDQWjEFe6Ekg==", + "version": "3.5.38", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.38.tgz", + "integrity": "sha512-DuA2GiZawSEW442iw/9+Fkol8hTgb4Ke5KkhmSry65QA7YuyMbIdy8p0XZRMvNwJdgRz307W8g1CSzdvS4nuNg==", + "license": "MIT", "dependencies": { - "@babel/parser": "^7.23.5", - "postcss": "^8.4.14", - "source-map": "^0.6.1" - }, - "optionalDependencies": { - "prettier": "^1.18.2 || ^2.0.0" + "@babel/parser": "^7.29.7", + "@vue/compiler-core": "3.5.38", + "@vue/compiler-dom": "3.5.38", + "@vue/compiler-ssr": "3.5.38", + "@vue/shared": "3.5.38", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.15", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.38", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.38.tgz", + "integrity": "sha512-7s+W5Gc42FGxZMcuwl8H5B29T8BJPMdBT7KHFE+BbAuZ/iTEdTtv7z2XiMjiaUUw4w3ZcCEdHs36RuYJ2VA7bA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.38", + "@vue/shared": "3.5.38" } }, - "node_modules/@vue/component-compiler-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@vue/component-compiler-utils/-/component-compiler-utils-3.3.0.tgz", - "integrity": "sha512-97sfH2mYNU+2PzGrmK2haqffDpVASuib9/w2/noxiFi31Z54hW+q3izKQXXQZSNhtiUpAI36uSuYepeBe4wpHQ==", - "dev": true, + "node_modules/@vue/devtools-api": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", + "license": "MIT" + }, + "node_modules/@vue/reactivity": { + "version": "3.5.38", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.38.tgz", + "integrity": "sha512-pG6LV/NDNRbKizcUjFFLAfjaL8mcv4DmR9avNcUw2gDHBzZneuS2TWCmp633ynzxz9YYKNeEPK2I8Wraqy2HUQ==", + "license": "MIT", "dependencies": { - "consolidate": "^0.15.1", - "hash-sum": "^1.0.2", - "lru-cache": "^4.1.2", - "merge-source-map": "^1.1.0", - "postcss": "^7.0.36", - "postcss-selector-parser": "^6.0.2", - "source-map": "~0.6.1", - "vue-template-es2015-compiler": "^1.9.0" - }, - "optionalDependencies": { - "prettier": "^1.18.2 || ^2.0.0" + "@vue/shared": "3.5.38" } }, - "node_modules/@vue/component-compiler-utils/node_modules/lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, + "node_modules/@vue/runtime-core": { + "version": "3.5.38", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.38.tgz", + "integrity": "sha512-iyW8WVfF1CpCXxncZY5Ei6rSd6oZr5DgEom//fUjRBRl56AXPD+s9ATvukRt77ZFTuYlnVA1bxY+dJB94tWVYw==", + "license": "MIT", "dependencies": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" + "@vue/reactivity": "3.5.38", + "@vue/shared": "3.5.38" } }, - "node_modules/@vue/component-compiler-utils/node_modules/picocolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", - "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", - "dev": true + "node_modules/@vue/runtime-dom": { + "version": "3.5.38", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.38.tgz", + "integrity": "sha512-apX2wt9sdfDshS+a2xueFZLVpt0GkRJZSoPmrW/SA4yzXTznhfcMVW59gr7h4YQeY0vJhdJkk2rsIDwgfFgC5A==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.38", + "@vue/runtime-core": "3.5.38", + "@vue/shared": "3.5.38", + "csstype": "^3.2.3" + } }, - "node_modules/@vue/component-compiler-utils/node_modules/postcss": { - "version": "7.0.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", - "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", - "dev": true, + "node_modules/@vue/server-renderer": { + "version": "3.5.38", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.38.tgz", + "integrity": "sha512-vue8vbf2QlV4quHqzwmJy6dWfmRhP1J8l4wtZg60CL6VoKqcPY2oe7may3+1d9qfpedjK5PRLFqd5k3Isj9mUw==", + "license": "MIT", "dependencies": { - "picocolors": "^0.2.1", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=6.0.0" + "@vue/compiler-ssr": "3.5.38", + "@vue/shared": "3.5.38" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" + "peerDependencies": { + "vue": "3.5.38" } }, - "node_modules/@vue/component-compiler-utils/node_modules/yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", - "dev": true + "node_modules/@vue/shared": { + "version": "3.5.38", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.38.tgz", + "integrity": "sha512-FTW0AFZNaK5/mOqvGBwVfUlNLU38TiQn4+DQgIFUnrBBJQ1crMJ82yeGQLV5jyKFsO8yRukpbuP7x+nRbH6aug==", + "license": "MIT" }, "node_modules/@vue/test-utils": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/@vue/test-utils/-/test-utils-1.3.6.tgz", - "integrity": "sha512-udMmmF1ts3zwxUJEIAj5ziioR900reDrt6C9H3XpWPsLBx2lpHKoA4BTdd9HNIYbkGltWw+JjWJ+5O6QBwiyEw==", + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/@vue/test-utils/-/test-utils-2.4.11.tgz", + "integrity": "sha512-GDqaqZsA6m2E5vNzej0aYiIb6BX8xV9pNSbbbXKOfEYwg7ZNblVX8suyqmUBThq8VIrgAJNxn+z72hVtUeiWHA==", "dev": true, + "license": "MIT", "dependencies": { - "dom-event-types": "^1.0.0", - "lodash": "^4.17.15", - "pretty": "^2.0.0" + "js-beautify": "^1.14.9", + "vue-component-type-helpers": "^3.0.0" }, "peerDependencies": { - "vue": "2.x", - "vue-template-compiler": "^2.x" + "@vue/compiler-dom": "3.x", + "@vue/server-renderer": "3.x", + "vue": "3.x" + }, + "peerDependenciesMeta": { + "@vue/server-renderer": { + "optional": true + } } }, "node_modules/@webassemblyjs/ast": { @@ -1615,10 +1668,14 @@ "dev": true }, "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", + "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } }, "node_modules/acorn": { "version": "8.12.1", @@ -1776,13 +1833,17 @@ } }, "node_modules/array-buffer-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", - "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "is-array-buffer": "^3.0.1" + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -1811,16 +1872,42 @@ } }, "node_modules/array.prototype.reduce": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.5.tgz", - "integrity": "sha512-kDdugMl7id9COE8R7MHF5jWk7Dqt/fs4Pv+JXoICnYwqpjjjbUurz6w5fT5IG6brLdJhv6/VoHB0H7oyIBXd+Q==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.8.tgz", + "integrity": "sha512-DwuEqgXFBwbmZSRqt3BpQigWNUoqw9Ml2dTWdF3B2zQlQX4OeUE0zyuzX0fX0IbTvjdkZbcBTU3idgpO78qkTw==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", "es-array-method-boxes-properly": "^1.0.0", - "is-string": "^1.0.7" + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "is-string": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" }, "engines": { "node": ">= 0.4" @@ -1862,6 +1949,16 @@ "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", "dev": true }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -1877,16 +1974,15 @@ "node": ">= 4.0.0" } }, - "node_modules/atoa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/atoa/-/atoa-1.0.0.tgz", - "integrity": "sha512-VVE1H6cc4ai+ZXo/CRWoJiHXrA1qfA31DPnx6D20+kSI547hQN5Greh51LQ1baMRMfxO5K5M4ImMtZbZt2DODQ==" - }, "node_modules/available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -2005,17 +2101,12 @@ "node": ">=8" } }, - "node_modules/bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true - }, "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/brace-expansion": { "version": "1.1.11", @@ -2132,16 +2223,47 @@ } }, "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", + "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==", "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "get-intrinsic": "^1.3.0", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", "dependencies": { - "es-define-property": "^1.0.0", "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" }, "engines": { "node": ">= 0.4" @@ -2336,6 +2458,7 @@ "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", "dev": true, + "license": "MIT", "dependencies": { "@types/q": "^1.5.1", "chalk": "^2.4.1", @@ -2405,51 +2528,17 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, - "node_modules/condense-newlines": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/condense-newlines/-/condense-newlines-0.2.1.tgz", - "integrity": "sha512-P7X+QL9Hb9B/c8HI5BFFKmjgBu2XpQuF98WZ9XkO+dBGgk5XgwiQz7o1SmpglNWId3581UcS0SFAWfoIhMHPfg==", - "dev": true, - "dependencies": { - "extend-shallow": "^2.0.1", - "is-whitespace": "^0.3.0", - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/config-chain": { "version": "1.1.13", "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", "dev": true, + "license": "MIT", "dependencies": { "ini": "^1.3.4", "proto-list": "~1.2.1" } }, - "node_modules/consolidate": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/consolidate/-/consolidate-0.15.1.tgz", - "integrity": "sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw==", - "dev": true, - "dependencies": { - "bluebird": "^3.1.1" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/contra": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/contra/-/contra-1.9.4.tgz", - "integrity": "sha512-N9ArHAqwR/lhPq4OdIAwH4e1btn6EIZMAz4TazjnzCiVECcWUPTma+dRAM38ERImEJBh8NiCCpjoQruSZ+agYg==", - "dependencies": { - "atoa": "1.0.0", - "ticky": "1.0.1" - } - }, "node_modules/convert-source-map": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", @@ -2479,10 +2568,11 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -2492,52 +2582,17 @@ "node": ">= 8" } }, - "node_modules/crossvent": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/crossvent/-/crossvent-1.5.4.tgz", - "integrity": "sha512-b6gEmNAh3kemyfNJ0LQzA/29A+YeGwevlSkNp2x0TzLOMYc0b85qRAD06OUuLWLQpR7HdJHNZQTlD1cfwoTrzg==", - "dependencies": { - "custom-event": "1.0.0" - } - }, "node_modules/crypto-js": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", - "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", - "dev": true - }, - "node_modules/css-loader": { - "version": "6.7.3", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.3.tgz", - "integrity": "sha512-qhOH1KlBMnZP8FzRO6YCH9UHXQhVMcEGLyNdb7Hv2cpcmJbW0YrddO+tG1ab5nT41KpHIYGsbeHqxB9xPu1pKQ==", - "dev": true, - "peer": true, - "dependencies": { - "icss-utils": "^5.1.0", - "postcss": "^8.4.19", - "postcss-modules-extract-imports": "^3.0.0", - "postcss-modules-local-by-default": "^4.0.0", - "postcss-modules-scope": "^3.0.0", - "postcss-modules-values": "^4.0.0", - "postcss-value-parser": "^4.2.0", - "semver": "^7.3.8" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - } + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==" }, "node_modules/css-select": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "boolbase": "^1.0.0", "css-what": "^3.2.1", @@ -2549,13 +2604,15 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/css-tree": { "version": "1.0.0-alpha.37", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==", "dev": true, + "license": "MIT", "dependencies": { "mdn-data": "2.0.4", "source-map": "^0.6.1" @@ -2569,6 +2626,7 @@ "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz", "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">= 6" }, @@ -2576,23 +2634,12 @@ "url": "https://github.com/sponsors/fb55" } }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true, - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/csso": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", "dev": true, + "license": "MIT", "dependencies": { "css-tree": "^1.1.2" }, @@ -2605,6 +2652,7 @@ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", "dev": true, + "license": "MIT", "dependencies": { "mdn-data": "2.0.14", "source-map": "^0.6.1" @@ -2617,17 +2665,14 @@ "version": "2.0.14", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", - "dev": true + "dev": true, + "license": "CC0-1.0" }, "node_modules/csstype": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", - "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" - }, - "node_modules/custom-event": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.0.tgz", - "integrity": "sha512-6nOXX3UitrmdvSJWoVR2dlzhbX5bEUqmqsMUyx1ypCLZkHHkcuYtdpW3p94RGvcFkTV7DkLo+Ilbwnlwi8L+jw==" + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" }, "node_modules/data-uri-to-buffer": { "version": "6.0.2", @@ -2638,11 +2683,59 @@ "node": ">= 14" } }, - "node_modules/de-indent": { + "node_modules/data-view-buffer": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", - "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==", - "dev": true + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/debug": { "version": "4.3.4", @@ -2730,11 +2823,13 @@ } }, "node_modules/define-properties": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", - "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "dev": true, + "license": "MIT", "dependencies": { + "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" }, @@ -2817,17 +2912,12 @@ "node": ">=6.0.0" } }, - "node_modules/dom-event-types": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/dom-event-types/-/dom-event-types-1.1.0.tgz", - "integrity": "sha512-jNCX+uNJ3v38BKvPbpki6j5ItVlnSqVV6vDWGS6rExzCMjsc39frLjm1n91o6YaKK6AZl0wLloItW6C6mr61BQ==", - "dev": true - }, "node_modules/dom-serializer": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", "dev": true, + "license": "MIT", "dependencies": { "domelementtype": "^2.0.1", "entities": "^2.0.0" @@ -2843,31 +2933,50 @@ "type": "github", "url": "https://github.com/sponsors/fb55" } - ] + ], + "license": "BSD-2-Clause" }, - "node_modules/domelementtype": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", - "dev": true + "node_modules/dom-serializer/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "license": "BSD-2-Clause", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", + "dev": true, + "license": "BSD-2-Clause" }, "node_modules/domutils": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "dom-serializer": "0", "domelementtype": "1" } }, - "node_modules/dragula": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/dragula/-/dragula-3.7.2.tgz", - "integrity": "sha512-iDPdNTPZY7P/l0CQ800QiX+PNA2XF9iC3ePLWfGxeb/j8iPPedRuQdfSOfZrazgSpmaShYvYQ/jx7keWb4YNzA==", + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", "dependencies": { - "contra": "1.9.4", - "crossvent": "1.5.4" + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" } }, "node_modules/eastasianwidth": { @@ -2877,44 +2986,59 @@ "dev": true }, "node_modules/editorconfig": { - "version": "0.15.3", - "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.15.3.tgz", - "integrity": "sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.7.tgz", + "integrity": "sha512-e0GOtq/aTQhVdNyDU9e02+wz9oDDM+SIOQxWME2QRjzRX5yyLAuHDE+0aE8vHb9XRC8XD37eO2u57+F09JqFhw==", "dev": true, + "license": "MIT", "dependencies": { - "commander": "^2.19.0", - "lru-cache": "^4.1.5", - "semver": "^5.6.0", - "sigmund": "^1.0.1" + "@one-ini/wasm": "0.1.1", + "commander": "^10.0.0", + "minimatch": "^9.0.1", + "semver": "^7.5.3" }, "bin": { "editorconfig": "bin/editorconfig" + }, + "engines": { + "node": ">=14" } }, - "node_modules/editorconfig/node_modules/lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "node_modules/editorconfig/node_modules/brace-expansion": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", + "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", "dev": true, + "license": "MIT", "dependencies": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" + "balanced-match": "^1.0.0" } }, - "node_modules/editorconfig/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "node_modules/editorconfig/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", "dev": true, - "bin": { - "semver": "bin/semver" + "license": "MIT", + "engines": { + "node": ">=14" } }, - "node_modules/editorconfig/node_modules/yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", - "dev": true + "node_modules/editorconfig/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, "node_modules/electron-to-chromium": { "version": "1.5.13", @@ -2960,10 +3084,13 @@ } }, "node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "dev": true, + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, "funding": { "url": "https://github.com/fb55/entities?sponsor=1" } @@ -2999,45 +3126,85 @@ } }, "node_modules/es-abstract": { - "version": "1.21.2", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.2.tgz", - "integrity": "sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==", + "version": "1.24.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.2.tgz", + "integrity": "sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==", "dev": true, + "license": "MIT", "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-set-tostringtag": "^2.0.1", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.2.0", - "get-symbol-description": "^1.0.0", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.5", - "is-array-buffer": "^3.0.2", + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.10", - "is-weakref": "^1.0.2", - "object-inspect": "^1.12.3", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.4.3", - "safe-regex-test": "^1.0.0", - "string.prototype.trim": "^1.2.7", - "string.prototype.trimend": "^1.0.6", - "string.prototype.trimstart": "^1.0.6", - "typed-array-length": "^1.0.4", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.9" + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-abstract-get": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-abstract-get/-/es-abstract-get-1.0.0.tgz", + "integrity": "sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.2", + "is-callable": "^1.2.7", + "object-inspect": "^1.13.4" }, "engines": { "node": ">= 0.4" @@ -3053,13 +3220,11 @@ "dev": true }, "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.4" - }, + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -3079,29 +3244,47 @@ "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==", "dev": true }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-set-tostringtag": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", - "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", "dev": true, + "license": "MIT", "dependencies": { - "get-intrinsic": "^1.1.3", - "has": "^1.0.3", - "has-tostringtag": "^1.0.0" + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" } }, "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.1.tgz", + "integrity": "sha512-CxN9N56HYfd2m/acc/NOFrZQsN9kU4eh+2kk6A707Kz1krH8tKmfrs5RnftB8WNX80T0NS7vSQsDOlg23diR2g==", "dev": true, + "license": "MIT", "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" + "es-abstract-get": "^1.0.0", + "es-errors": "^1.3.0", + "is-callable": "^1.2.7", + "is-date-object": "^1.1.0", + "is-symbol": "^1.1.1" }, "engines": { "node": ">= 0.4" @@ -3462,6 +3645,12 @@ "node": ">=4.0" } }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -3480,18 +3669,6 @@ "node": ">=0.8.x" } }, - "node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/extract-zip": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", @@ -3675,12 +3852,19 @@ "dev": true }, "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", "dev": true, + "license": "MIT", "dependencies": { - "is-callable": "^1.1.3" + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/foreground-child": { @@ -3907,6 +4091,21 @@ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -3917,15 +4116,21 @@ } }, "node_modules/function.prototype.name": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", - "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.2.0.tgz", + "integrity": "sha512-jObKIik1P2QjPHP5nz5BaOtUlfgS0fWo8IUByNXkM+o+02sJOi94em77GwJKQSJ3gfPHdgzLNrHc1uokV4P/ew==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", - "functions-have-names": "^1.2.2" + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2", + "hasown": "^2.0.4", + "is-callable": "^1.2.7", + "is-document.all": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -3939,6 +4144,7 @@ "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -3971,16 +4177,22 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "dev": true, + "license": "MIT", "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -3998,6 +4210,20 @@ "node": ">=8.0.0" } }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-stream": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", @@ -4014,13 +4240,15 @@ } }, "node_modules/get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" @@ -4124,12 +4352,14 @@ } }, "node_modules/globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", "dev": true, + "license": "MIT", "dependencies": { - "define-properties": "^1.1.3" + "define-properties": "^1.2.1", + "gopd": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -4159,12 +4389,13 @@ } }, "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.3" + "license": "MIT", + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -4195,10 +4426,14 @@ } }, "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -4225,10 +4460,14 @@ } }, "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -4237,10 +4476,11 @@ } }, "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -4249,12 +4489,13 @@ } }, "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dev": true, + "license": "MIT", "dependencies": { - "has-symbols": "^1.0.2" + "has-symbols": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -4264,10 +4505,11 @@ } }, "node_modules/hash-sum": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz", - "integrity": "sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA==", - "dev": true + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-2.0.0.tgz", + "integrity": "sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==", + "dev": true, + "license": "MIT" }, "node_modules/hasha": { "version": "5.2.2", @@ -4286,10 +4528,11 @@ } }, "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", "dev": true, + "license": "MIT", "dependencies": { "function-bind": "^1.1.2" }, @@ -4347,19 +4590,6 @@ "node": ">= 14" } }, - "node_modules/icss-utils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", - "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", - "dev": true, - "peer": true, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -4468,17 +4698,19 @@ "version": "1.3.8", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/internal-slot": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", - "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", "dev": true, + "license": "MIT", "dependencies": { - "get-intrinsic": "^1.2.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -4529,14 +4761,18 @@ } }, "node_modules/is-array-buffer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", - "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "is-typed-array": "^1.1.10" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -4548,13 +4784,37 @@ "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", "dev": true, + "license": "MIT", "dependencies": { - "has-bigints": "^1.0.1" + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -4573,13 +4833,14 @@ } }, "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -4588,12 +4849,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, "node_modules/is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -4618,13 +4873,33 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", "dev": true, + "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -4633,13 +4908,20 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "node_modules/is-document.all": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-document.all/-/is-document.all-1.0.0.tgz", + "integrity": "sha512-+XSoyS05OdBbhFuELhgTCpFNHkpBOJqtsZfUFFpe5QTw+9Sjbh8zitxhQkYAo6wV7e1Vb8cAPvpCk9jGam/82g==", "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.4" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-extglob": { @@ -4651,10 +4933,26 @@ "node": ">=0.10.0" } }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, "engines": { "node": ">=8" @@ -4687,11 +4985,25 @@ "node": ">=0.10.0" } }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -4709,12 +5021,14 @@ } }, "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", "dev": true, + "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -4754,14 +5068,30 @@ } }, "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -4770,12 +5100,16 @@ } }, "node_modules/is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2" + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -4794,12 +5128,14 @@ } }, "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", "dev": true, + "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -4809,12 +5145,15 @@ } }, "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", "dev": true, + "license": "MIT", "dependencies": { - "has-symbols": "^1.0.2" + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -4824,16 +5163,13 @@ } }, "node_modules/is-typed-array": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", - "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", "dev": true, + "license": "MIT", "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" + "which-typed-array": "^1.1.16" }, "engines": { "node": ">= 0.4" @@ -4860,25 +5196,50 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2" + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-whitespace": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/is-whitespace/-/is-whitespace-0.3.0.tgz", - "integrity": "sha512-RydPhl4S6JwAyj0JJjshWJEFG6hNye3pZFBRZaTUfZFwGHxzppNaNOVgQuS/E/SlhrApuMXrpnK1EEIXfdo3Dg==", + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-windows": { @@ -5098,15 +5459,17 @@ } }, "node_modules/js-beautify": { - "version": "1.14.7", - "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.14.7.tgz", - "integrity": "sha512-5SOX1KXPFKx+5f6ZrPsIPEY7NwKeQz47n3jm2i+XeHx9MoRsfQenlOP13FQhWvg8JRS0+XLO6XYUQ2GX+q+T9A==", + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.4.tgz", + "integrity": "sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==", "dev": true, + "license": "MIT", "dependencies": { "config-chain": "^1.1.13", - "editorconfig": "^0.15.3", - "glob": "^8.0.3", - "nopt": "^6.0.0" + "editorconfig": "^1.0.4", + "glob": "^10.4.2", + "js-cookie": "^3.0.5", + "nopt": "^7.2.1" }, "bin": { "css-beautify": "js/bin/css-beautify.js", @@ -5114,49 +5477,94 @@ "js-beautify": "js/bin/js-beautify.js" }, "engines": { - "node": ">=10" + "node": ">=14" } }, "node_modules/js-beautify/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", + "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } }, - "node_modules/js-beautify/node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "node_modules/js-beautify/node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", "dev": true, + "license": "ISC", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" }, "engines": { - "node": ">=12" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/js-beautify/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/js-beautify/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, + "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^2.0.2" }, "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/js-beautify/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/js-cookie": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.8.tgz", + "integrity": "sha512-yeJd4aNAdYZQjaon2bpD/Gb0B/omw7HQOsynXXcOiWVCacbBcPlgn8S/d1X6blFSaHao7ozqtW7NZW19xpCtIw==", + "dev": true, + "license": "MIT" + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -5262,18 +5670,6 @@ "json-buffer": "3.0.1" } }, - "node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -5465,6 +5861,15 @@ "yallist": "^3.0.2" } }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, "node_modules/make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -5489,11 +5894,22 @@ "semver": "bin/semver.js" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/mdn-data": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==", - "dev": true + "dev": true, + "license": "CC0-1.0" }, "node_modules/memfs": { "version": "3.4.13", @@ -5507,15 +5923,6 @@ "node": ">= 4.0.0" } }, - "node_modules/merge-source-map": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", - "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", - "dev": true, - "dependencies": { - "source-map": "^0.6.1" - } - }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -5603,6 +6010,7 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -5727,6 +6135,7 @@ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, + "license": "MIT", "dependencies": { "minimist": "^1.2.6" }, @@ -5999,18 +6408,19 @@ "dev": true }, "node_modules/nopt": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz", - "integrity": "sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==", + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", + "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", "dev": true, + "license": "ISC", "dependencies": { - "abbrev": "^1.0.0" + "abbrev": "^2.0.0" }, "bin": { "nopt": "bin/nopt.js" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/normalize-path": { @@ -6332,6 +6742,7 @@ "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "boolbase": "~1.0.0" } @@ -6538,10 +6949,11 @@ } }, "node_modules/object-inspect": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", - "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -6559,14 +6971,17 @@ } }, "node_modules/object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "has-symbols": "^1.0.3", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", "object-keys": "^1.1.1" }, "engines": { @@ -6577,32 +6992,38 @@ } }, "node_modules/object.getownpropertydescriptors": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.5.tgz", - "integrity": "sha512-yDNzckpM6ntyQiGTik1fKV1DcVDRS+w8bvpWNCBanvH5LfRX9O8WTHqQzG4RZwRAM4I0oU7TV11Lj5v0g20ibw==", + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.9.tgz", + "integrity": "sha512-mt8YM6XwsTTovI+kdZdHSxoyF2DI59up034orlC9NfweclcWOt7CVascNNLp6U+bjFVCVCIh9PwS76tDM/rH8g==", "dev": true, + "license": "MIT", "dependencies": { - "array.prototype.reduce": "^1.0.5", - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "array.prototype.reduce": "^1.0.8", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.0", + "es-object-atoms": "^1.1.1", + "gopd": "^1.2.0", + "safe-array-concat": "^1.1.3" }, "engines": { - "node": ">= 0.8" + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/object.values": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", - "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -6637,6 +7058,24 @@ "node": ">= 0.8.0" } }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -6860,9 +7299,10 @@ "dev": true }, "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.1", @@ -6940,10 +7380,20 @@ "node": ">=8" } }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/postcss": { - "version": "8.4.32", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.32.tgz", - "integrity": "sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==", + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", "funding": [ { "type": "opencollective", @@ -6958,108 +7408,27 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" + "nanoid": "^3.3.12", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" } }, - "node_modules/postcss-modules-extract-imports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", - "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", - "dev": true, - "peer": true, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-local-by-default": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", - "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", - "dev": true, - "peer": true, - "dependencies": { - "icss-utils": "^5.0.0", - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-scope": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", - "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", - "dev": true, - "peer": true, - "dependencies": { - "postcss-selector-parser": "^6.0.4" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-values": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", - "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", - "dev": true, - "peer": true, - "dependencies": { - "icss-utils": "^5.0.0" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-selector-parser": { - "version": "6.0.11", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz", - "integrity": "sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==", - "dev": true, - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true, - "peer": true - }, "node_modules/postcss/node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "version": "3.3.14", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.14.tgz", + "integrity": "sha512-U9kYi5bpVMEI31yC8iw4bJJp0avcHXA0W8/wNfLfnvJYzihQo2ZRPYPvpAAd570HAcCBjCTN7vnr+v4StKl1IQ==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -7080,7 +7449,7 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", - "devOptional": true, + "dev": true, "bin": { "prettier": "bin-prettier.js" }, @@ -7088,20 +7457,6 @@ "node": ">=10.13.0" } }, - "node_modules/pretty": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pretty/-/pretty-2.0.0.tgz", - "integrity": "sha512-G9xUchgTEiNpormdYBl+Pha50gOUovT18IvAe7EYMZ1/f9W/WWMPRn+xI68yXNMUk3QXHDwo/1wV/4NejVNe1w==", - "dev": true, - "dependencies": { - "condense-newlines": "^0.2.1", - "extend-shallow": "^2.0.1", - "js-beautify": "^1.6.12" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -7136,7 +7491,8 @@ "version": "1.2.4", "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/proxy-agent": { "version": "6.4.0", @@ -7172,12 +7528,6 @@ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "dev": true }, - "node_modules/pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", - "dev": true - }, "node_modules/pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -7297,7 +7647,9 @@ "version": "1.5.1", "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", + "deprecated": "You or someone you depend on is using Q, the JavaScript Promise library that gave JavaScript developers strong feelings about promises. They can almost certainly migrate to the native JavaScript promise now. Thank you literally everyone for joining me in this bet against the odds. Be excellent to each other.\n\n(For a CapTP with native promises, see @endo/eventual-send and @endo/captp)", "dev": true, + "license": "MIT", "engines": { "node": ">=0.6.0", "teleport": ">=0.2.0" @@ -7401,15 +7753,42 @@ "node": ">= 10.13.0" } }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/regexp.prototype.flags": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", - "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "functions-have-names": "^1.2.2" + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -7541,6 +7920,33 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/safe-array-concat": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.4.tgz", + "integrity": "sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "get-intrinsic": "^1.3.0", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-array-concat/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -7561,15 +7967,43 @@ } ] }, - "node_modules/safe-regex-test": { + "node_modules/safe-push-apply": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", - "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "is-regex": "^1.1.4" + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-push-apply/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -7596,7 +8030,8 @@ "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/schema-utils": { "version": "3.3.0", @@ -7681,6 +8116,37 @@ "node": ">= 0.4" } }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/shallow-clone": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", @@ -7724,15 +8190,17 @@ } }, "node_modules/side-channel": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -7741,11 +8209,61 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/sigmund": { + "node_modules/side-channel-list": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", - "integrity": "sha512-fCvEXfh6NWpm+YSuY2bpXb/VIihqWA6hLsgboC+0nl71Q7N7o2eaCW8mJa/NLvQhs6jpd3VZV4UiUQlV6+lc8g==", - "dev": true + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/signal-exit": { "version": "3.0.7", @@ -7935,18 +8453,26 @@ "node": ">= 14" } }, + "node_modules/sortablejs": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.14.0.tgz", + "integrity": "sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w==", + "license": "MIT" + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -8011,7 +8537,22 @@ "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility", - "dev": true + "dev": true, + "license": "MIT" + }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } }, "node_modules/stream-browserify": { "version": "3.0.0", @@ -8076,14 +8617,20 @@ } }, "node_modules/string.prototype.trim": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz", - "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==", + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.11.tgz", + "integrity": "sha512-PwvK7BU+CMTJGYQCTZb5RWXIML92lftJLhQz1tBzgKiqGxJaMlBAa48POXaNAC2s4y8jr3EFqrkF9+44neS46w==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.2", + "es-object-atoms": "^1.1.2", + "has-property-descriptors": "^1.0.2", + "safe-regex-test": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -8093,28 +8640,37 @@ } }, "node_modules/string.prototype.trimend": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", - "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.10.tgz", + "integrity": "sha512-2+3aDAOmPTmuFwjDnmJG2ctEkQKVki7vOSqaxkv42Mowj1V6PnvuwFCRrR5lChUux1TBskPjfkeTOhqczDMxTw==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/string.prototype.trimstart": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", - "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -8210,24 +8766,13 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/svg-to-vue": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/svg-to-vue/-/svg-to-vue-0.7.0.tgz", - "integrity": "sha512-Tg2nMmf3BQorYCAjxbtTkYyWPVSeox5AZUFvfy4MoWK/5tuQlnA/h3LAlTjV3sEvOC5FtUNovRSj3p784l4KOA==", - "dev": true, - "dependencies": { - "svgo": "^1.3.2" - }, - "peerDependencies": { - "vue-template-compiler": "^2.0.0" - } - }, "node_modules/svgo": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz", "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==", "deprecated": "This SVGO version is no longer supported. Upgrade to v2.x.x.", "dev": true, + "license": "MIT", "dependencies": { "chalk": "^2.4.1", "coa": "^2.0.2", @@ -8421,20 +8966,6 @@ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "dev": true }, - "node_modules/ticky": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ticky/-/ticky-1.0.1.tgz", - "integrity": "sha512-RX35iq/D+lrsqhcPWIazM9ELkjOe30MSeoBHQHSsRwd1YuhJO5ui1K1/R0r7N3mFvbLBs33idw+eR6j+w6i/DA==" - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -8578,15 +9109,79 @@ "node": ">=8" } }, - "node_modules/typed-array-length": { + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", - "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", "for-each": "^0.3.3", - "is-typed-array": "^1.1.9" + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.8.tgz", + "integrity": "sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "for-each": "^0.3.5", + "gopd": "^1.2.0", + "is-typed-array": "^1.1.15", + "possible-typed-array-names": "^1.1.0", + "reflect.getprototypeof": "^1.0.10" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -8605,7 +9200,7 @@ "version": "5.0.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.3.tgz", "integrity": "sha512-xv8mOEDnigb/tN9PSMTwSEqAnUvkoXMQlicOb0IUVDBSQCgBSaAAROUZYy2IcUy5qU6XajK5jjjO7TMWqBTKZA==", - "dev": true, + "devOptional": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -8615,15 +9210,19 @@ } }, "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", + "call-bound": "^1.0.3", "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -8676,7 +9275,8 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", "integrity": "sha512-vRCqFv6UhXpWxZPyGDh/F3ZpNv8/qo7w6iufLpQg9aKnQ71qM4B5KiI7Mia9COcjEhrO9LueHpMYjYzsWH3OIg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/update-browserslist-db": { "version": "1.1.0", @@ -8780,6 +9380,7 @@ "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz", "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==", "dev": true, + "license": "MIT", "dependencies": { "define-properties": "^1.1.3", "es-abstract": "^1.17.2", @@ -8791,180 +9392,169 @@ } }, "node_modules/vue": { - "version": "2.7.16", - "resolved": "https://registry.npmjs.org/vue/-/vue-2.7.16.tgz", - "integrity": "sha512-4gCtFXaAA3zYZdTp5s4Hl2sozuySsgz4jy1EnpBHNfpMa9dK1ZCG7viqBPCwXtmgc8nHqUsAu3G4gtmXkkY3Sw==", - "deprecated": "Vue 2 has reached EOL and is no longer actively maintained. See https://v2.vuejs.org/eol/ for more details.", + "version": "3.5.38", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.38.tgz", + "integrity": "sha512-vAMKHfImQlYSy0C+PBue4s3ERZ2xGKfgZg5GXAsLInq1dyh2H78ILVP5sK0KPFPVW4kv+OGCIvBEondcjpZp7A==", + "license": "MIT", "dependencies": { - "@vue/compiler-sfc": "2.7.16", - "csstype": "^3.1.0" + "@vue/compiler-dom": "3.5.38", + "@vue/compiler-sfc": "3.5.38", + "@vue/runtime-dom": "3.5.38", + "@vue/server-renderer": "3.5.38", + "@vue/shared": "3.5.38" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/vue-hot-reload-api": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz", - "integrity": "sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog==", - "dev": true + "node_modules/vue-component-type-helpers": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/vue-component-type-helpers/-/vue-component-type-helpers-3.3.5.tgz", + "integrity": "sha512-Fe1jyPJoUGpJOYKOri44jduR7My4yYINOMJISuMAbmrs+L5LbIDUc8NTWZYY3EJLK0yPLuCmcd5zoCsE4k2/KA==", + "dev": true, + "license": "MIT" }, "node_modules/vue-loader": { - "version": "15.11.1", - "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.11.1.tgz", - "integrity": "sha512-0iw4VchYLePqJfJu9s62ACWUXeSqM30SQqlIftbYWM3C+jpPcEHKSPUZBLjSF9au4HTHQ/naF6OGnO3Q/qGR3Q==", + "version": "17.4.2", + "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-17.4.2.tgz", + "integrity": "sha512-yTKOA4R/VN4jqjw4y5HrynFL8AK0Z3/Jt7eOJXEitsm0GMRHDBjCfCiuTiLP7OESvsZYo2pATCWhDqxC5ZrM6w==", "dev": true, + "license": "MIT", "dependencies": { - "@vue/component-compiler-utils": "^3.1.0", - "hash-sum": "^1.0.2", - "loader-utils": "^1.1.0", - "vue-hot-reload-api": "^2.3.0", - "vue-style-loader": "^4.1.0" + "chalk": "^4.1.0", + "hash-sum": "^2.0.0", + "watchpack": "^2.4.0" }, "peerDependencies": { - "css-loader": "*", - "webpack": "^3.0.0 || ^4.1.0 || ^5.0.0-0" + "webpack": "^4.1.0 || ^5.0.0-0" }, "peerDependenciesMeta": { - "cache-loader": { - "optional": true - }, - "prettier": { + "@vue/compiler-sfc": { "optional": true }, - "vue-template-compiler": { + "vue": { "optional": true } } }, - "node_modules/vue-loader/node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "node_modules/vue-loader/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "license": "MIT", "dependencies": { - "minimist": "^1.2.0" + "color-convert": "^2.0.1" }, - "bin": { - "json5": "lib/cli.js" + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/vue-loader/node_modules/loader-utils": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", - "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "node_modules/vue-loader/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, + "license": "MIT", "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/vue-style-loader": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.3.tgz", - "integrity": "sha512-sFuh0xfbtpRlKfm39ss/ikqs9AbKCoXZBpHeVZ8Tx650o0k0q/YCM7FRvigtxpACezfq6af+a7JeqVTWvncqDg==", - "dev": true, - "dependencies": { - "hash-sum": "^1.0.2", - "loader-utils": "^1.0.2" - } - }, - "node_modules/vue-style-loader/node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": true, - "dependencies": { - "minimist": "^1.2.0" + "node": ">=10" }, - "bin": { - "json5": "lib/cli.js" + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/vue-style-loader/node_modules/loader-utils": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", - "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "node_modules/vue-loader/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, + "license": "MIT", "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" + "color-name": "~1.1.4" }, "engines": { - "node": ">=4.0.0" + "node": ">=7.0.0" } }, - "node_modules/vue-svg-loader": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/vue-svg-loader/-/vue-svg-loader-0.16.0.tgz", - "integrity": "sha512-2RtFXlTCYWm8YAEO2qAOZ2SuIF2NvLutB5muc3KDYoZq5ZeCHf8ggzSan3ksbbca7CJ/Aw57ZnDF4B7W/AkGtw==", + "node_modules/vue-loader/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true, - "dependencies": { - "loader-utils": "^1.2.3", - "svg-to-vue": "^0.7.0" - }, - "peerDependencies": { - "vue-template-compiler": "^2.0.0" - } + "license": "MIT" }, - "node_modules/vue-svg-loader/node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "node_modules/vue-loader/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" + "license": "MIT", + "engines": { + "node": ">=8" } }, - "node_modules/vue-svg-loader/node_modules/loader-utils": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", - "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "node_modules/vue-loader/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "license": "MIT", "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=4.0.0" + "node": ">=8" } }, - "node_modules/vue-template-compiler": { - "version": "2.7.16", - "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.16.tgz", - "integrity": "sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==", + "node_modules/vue-svg-loader": { + "version": "0.17.0-beta.2", + "resolved": "https://registry.npmjs.org/vue-svg-loader/-/vue-svg-loader-0.17.0-beta.2.tgz", + "integrity": "sha512-iMUGJTKEcuNAG8VXOchjA8443IqEmEi2Aw6EVIHWma2cC4TUQ7Oet5Yry9IFfqXQXXvyzXz5EyttVvfRGTNH4Q==", "dev": true, + "license": "MIT", "dependencies": { - "de-indent": "^1.0.2", - "he": "^1.2.0" + "loader-utils": "^2.0.0", + "semver": "^7.3.2", + "svgo": "^1.3.2" + }, + "peerDependencies": { + "vue": "^2.5.0 || ^3.0.0-0" } }, - "node_modules/vue-template-es2015-compiler": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz", - "integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==", - "dev": true - }, - "node_modules/vue2-dragula": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/vue2-dragula/-/vue2-dragula-2.5.5.tgz", - "integrity": "sha512-y+s2S1s6p11ds5ay6kWgAzxmXa4LwM8HBrQG+q8+rPehrmOlV/kvEyNidEYL+glskofL5vTGhno4xGYfg+wm3Q==", + "node_modules/vuedraggable": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/vuedraggable/-/vuedraggable-4.1.0.tgz", + "integrity": "sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==", + "license": "MIT", "dependencies": { - "dragula": "3.7.2" + "sortablejs": "1.14.0" + }, + "peerDependencies": { + "vue": "^3.0.1" } }, "node_modules/vuex": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/vuex/-/vuex-3.6.2.tgz", - "integrity": "sha512-ETW44IqCgBpVomy520DT5jf8n0zoCac+sxWnn+hMe/CzaSejb/eVw2YToiXYX+Ex/AuHHia28vWTq4goAexFbw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/vuex/-/vuex-4.1.0.tgz", + "integrity": "sha512-hmV6UerDrPcgbSy9ORAtNXDr9M4wlNP4pEFKye4ujJF8oqgFFuxDCdOLS3eNoRTtq5O3hoBDh9Doj1bQMYHRbQ==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^6.0.0-beta.11" + }, "peerDependencies": { - "vue": "^2.0.0" + "vue": "^3.2.0" } }, "node_modules/watchpack": { @@ -9118,16 +9708,74 @@ } }, "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/which-collection": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", "dev": true, + "license": "MIT", "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -9140,17 +9788,19 @@ "dev": true }, "node_modules/which-typed-array": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", - "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "version": "1.1.22", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.22.tgz", + "integrity": "sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==", "dev": true, + "license": "MIT", "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.10" + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" diff --git a/package.json b/package.json index 4970fa5be..dffba57c6 100644 --- a/package.json +++ b/package.json @@ -27,17 +27,18 @@ "@types/chai": "^4.2.14", "@types/chrome": "^0.0.266", "@types/crypto-js": "^4.1.1", + "@types/lodash": "^4.14.166", "@types/mocha": "^10.0.6", "@types/sinon": "^17.0.2", "@types/sinon-chai": "^3.2.12", "@types/sinon-chrome": "^2.2.10", "@typescript-eslint/eslint-plugin": "^7.15.0", "@typescript-eslint/parser": "^7.15.0", - "@vue/test-utils": "^1.1.1", + "@vue/compiler-sfc": "^3.4.21", + "@vue/test-utils": "^2.4.5", "base64-loader": "^1.0.0", "buffer": "^6.0.3", "chai": "^4.2.0", - "crypto-js": "^4.1.1", "eslint": "^8.56.0", "fork-ts-checker-webpack-plugin": "^6.5.3", "lodash": "^4.17.21", @@ -56,22 +57,21 @@ "typescript": "^5.0.0", "url-loader": "^4.0.0", "util": "^0.12.5", - "vue-loader": "^15.10.1", - "vue-svg-loader": "^0.16.0", - "vue-template-compiler": "^2.7.16", + "vue-loader": "^17.4.2", + "vue-svg-loader": "0.17.0-beta.2", "webpack": "^5.94.0", "webpack-cli": "^5.0.0", "webpack-merge": "^5.0.0" }, "dependencies": { - "@types/lodash": "^4.14.166", "argon2-browser": "^1.18.0", + "crypto-js": "^4.1.1", "jsqr": "^1.3.1", "node-gost-crypto": "^1.0.2", "qrcode-generator": "^1.4.4", "qrcode-reader": "^1.0.4", - "vue": "^2.7.16", - "vue2-dragula": "^2.5.4", - "vuex": "^3.4.0" + "vue": "^3.4.21", + "vuedraggable": "^4.1.0", + "vuex": "^4.1.0" } } diff --git a/privacy-policy.md b/privacy-policy.md new file mode 100644 index 000000000..dd8a2775e --- /dev/null +++ b/privacy-policy.md @@ -0,0 +1,57 @@ +# Privacy Policy — OTPilot Authenticator + +_Last updated: 2026-06-26_ + +OTPilot Authenticator ("the extension") is a browser extension that generates two-factor +authentication (2FA) one-time codes. This policy explains what data the extension handles +and how. + +## What we collect + +We — the developer — **do not collect, receive, or transmit any of your data**. There are +no analytics, no tracking, and no telemetry. The extension has no server of its own. + +## Data the extension stores on your device + +The extension stores the following **locally**, in your browser's extension storage: + +- Your 2FA accounts: issuer/label and the account secret. +- Your preferences (e.g. theme, auto-lock timeout). + +If you set a master password, account secrets are encrypted with **Argon2id** key +derivation and **AES-GCM** before being stored. The encrypted vault auto-locks after the +idle period you configure. + +## Optional cloud backup + +If you explicitly enable cloud backup, the extension uploads your **encrypted** vault to a +cloud account **you own and authorize** — Dropbox. The backup is encrypted with your master +password; the developer cannot read it and never receives a copy. The extension connects +only to Dropbox's official API endpoints, and only after you opt in. You can disconnect at +any time. + +## Autofill + +When you trigger autofill, the current code is inserted into the active tab's login field, +**only** when the page's host matches the saved account. No page content is read, stored, +or transmitted to the developer. + +## Permissions + +The extension requests only the permissions needed for the above functions (storage, +active-tab access for autofill, scripting for autofill, identity for cloud-backup OAuth, +and alarms for auto-lock). Cloud-provider host access is requested optionally, only when +you enable backup. + +## Data sharing + +We do not sell, share, or transfer your data to any third party. The only data that leaves +your device is the encrypted backup you choose to upload to your own cloud account. + +## Changes + +We may update this policy; the "Last updated" date will change accordingly. + +## Contact + +Questions: kyvevcwmm@mozmail.com diff --git a/sass/_tokens.scss b/sass/_tokens.scss new file mode 100644 index 000000000..ec7e37a45 --- /dev/null +++ b/sass/_tokens.scss @@ -0,0 +1,112 @@ +// Design tokens — CSS custom properties. +// Replaces the old SCSS $themes map + themify mixin (see _ui.scss). +// Light is the :root default; theme classes on the app root override it. + +:root { + // Surfaces + --canvas: #e7e5df; + --app-bg: #ffffff; + --header: #ffffff; + --row: #f6f6f3; + --row-hover: #eeeeea; + + // Lines + --border: rgba(20, 20, 18, 0.08); + --border-strong: rgba(20, 20, 18, 0.16); + + // Text + --text: #1a1a17; + --text-dim: #6c6c66; + --text-faint: #a3a39a; + --label: #8a8a82; + + // Accent (hue 258 — cobalt) + --accent: oklch(0.53 0.19 258); + --accent-fg: #ffffff; + --accent-soft: oklch(0.95 0.04 258); + + // Status + --ok: oklch(0.64 0.15 155); + --warn: oklch(0.76 0.15 70); + --warn-soft: oklch(0.96 0.05 70); + --danger: oklch(0.6 0.2 25); + + // Misc + --ring-track: rgba(20, 20, 18, 0.1); + --shadow: 0 14px 40px rgba(20, 20, 18, 0.16); + --overlay: rgba(20, 20, 18, 0.5); // dim scrim over the app (modals) + --scrim: rgba(255, 255, 255, 0.5); // light wash (QR overlay) + + // Type & shape + --font-sans: system-ui, -apple-system, "Segoe UI", "Microsoft JhengHei", + "Microsoft YaHei", Roboto, Helvetica, Arial, sans-serif; + --font-mono: ui-monospace, "SF Mono", "JetBrains Mono", "Cascadia Code", + "Roboto Mono", Consolas, "Liberation Mono", monospace; + --radius-card: 22px; + --radius-row: 14px; + --radius-pill: 999px; +} + +@mixin dark-tokens { + --canvas: #19191c; + --app-bg: #1a1a1e; + --header: #1e1e22; + --row: #222227; + --row-hover: #2a2a30; + --border: rgba(255, 255, 255, 0.08); + --border-strong: rgba(255, 255, 255, 0.16); + --text: #f1f1ef; + --text-dim: #9b9ba2; + --text-faint: #6a6a72; + --label: rgba(255, 255, 255, 0.5); + --accent: oklch(0.72 0.15 258); + --accent-fg: #16161a; + --accent-soft: oklch(0.3 0.07 258); + --ok: oklch(0.72 0.15 155); + --warn: oklch(0.8 0.14 70); + --warn-soft: oklch(0.32 0.06 70); + --danger: oklch(0.68 0.19 25); + --ring-track: rgba(255, 255, 255, 0.12); + --shadow: 0 18px 48px rgba(0, 0, 0, 0.55); + --overlay: rgba(0, 0, 0, 0.6); + --scrim: rgba(0, 0, 0, 0.5); +} + +.theme-dark { + @include dark-tokens; +} + +// auto = light by default, dark when the OS asks for it +.theme-auto { + @media (prefers-color-scheme: dark) { + @include dark-tokens; + } +} + +// High-contrast (was the old "accessibility" theme): white-on-black + yellow. +.theme-accessibility { + --canvas: #000; + --app-bg: #000; + --header: #000; + --row: #000; + --row-hover: #1a1a1a; + --border: #fff; + --border-strong: #fff; + --text: #fff; + --text-dim: #fff; + --text-faint: #fff; + --label: #fff; + --accent: #ffff00; + --accent-fg: #000; + --accent-soft: #333300; + --ok: #33ff66; + --warn: #ffd400; + --warn-soft: #332600; + --danger: #ff5040; + --ring-track: rgba(255, 255, 255, 0.45); + --shadow: none; + --overlay: rgba(0, 0, 0, 0.85); + --scrim: rgba(0, 0, 0, 0.7); +} + +// Compact reuses the light palette; only density changes (see popup.scss). diff --git a/sass/_ui.scss b/sass/_ui.scss index d33693e17..5f79f2a43 100644 --- a/sass/_ui.scss +++ b/sass/_ui.scss @@ -1,217 +1,56 @@ -// Re-usable ui components +// Re-usable ui components. +// Theming is CSS custom properties (see _tokens.scss). -// Colors -// go from darkest to lightest - -$themes: ( - normal: ( - black-1: black, - black-transparent: rgba(0, 0, 0, 0.5), - white-1: white, - white-transparent: rgba(255, 255, 255, 0.5), - grey-1: grey, - grey-2: #ccc, - grey-3: #eee, - grey-background: #eee, - blue-1: #08c, - yellow-1: #fff1ba, - yellow-2: #fff4cc, - red-1: #dd4b39, - red-2: #eea59c, - black-search: #2a2a2e, - white-search: #f9f9fa, - grey-search: #b1b1b3, - blue-menu: #f4fcff, - ), - accessibility: ( - black-1: white, - black-transparent: rgba(255, 255, 255, 1), - white-1: black, - white-transparent: rgba(0, 0, 0, 0.5), - grey-1: white, - grey-2: white, - grey-3: white, - grey-background: black, - blue-1: yellow, - yellow-1: yellow, - yellow-2: yellow, - red-1: red, - red-2: red, - black-search: white, - white-search: black, - grey-search: white, - blue-menu: cyan, - ), - dark: ( - black-1: #ccc, - black-transparent: rgba(255, 255, 255, 0.5), - white-1: #242424, - white-transparent: rgba(0, 0, 0, 0.5), - grey-1: grey, - grey-2: rgba(255, 255, 255, 0.15), - grey-3: #444, - grey-background: #1e1e1e, - blue-1: white, - yellow-1: rgba(255, 255, 255, 0.5), - yellow-2: rgba(255, 255, 255, 0.35), - red-1: #dd4b39, - red-2: #61221a, - black-search: white, - white-search: #202020, - grey-search: rgba(255, 255, 255, 0.35), - blue-menu: #2a2d2e, - ), - simple: ( - black-1: black, - black-transparent: rgba(0, 0, 0, 0.5), - white-1: white, - white-transparent: rgba(255, 255, 255, 0.5), - grey-1: grey, - grey-2: #ccc, - grey-3: #eee, - grey-background: #fff, - blue-1: #08c, - yellow-1: #fff1ba, - yellow-2: #fff4cc, - red-1: #dd4b39, - red-2: #eea59c, - black-search: #2a2a2e, - white-search: #f9f9fa, - grey-search: #b1b1b3, - blue-menu: #f4fcff, - ), - compact: ( - black-1: black, - black-transparent: rgba(0, 0, 0, 0.5), - white-1: white, - white-transparent: rgba(255, 255, 255, 0.5), - grey-1: grey, - grey-2: #ccc, - grey-3: #eee, - grey-background: #fff, - blue-1: #08c, - yellow-1: #fff1ba, - yellow-2: #fff4cc, - red-1: #dd4b39, - red-2: #eea59c, - black-search: #2a2a2e, - white-search: #f9f9fa, - grey-search: #b1b1b3, - blue-menu: #f4fcff, - ), - flat: ( - black-1: black, - black-transparent: rgba(0, 0, 0, 0.5), - white-1: white, - white-transparent: rgba(255, 255, 255, 0.5), - grey-1: grey, - grey-2: #ccc, - grey-3: #eee, - grey-background: #eee, - blue-1: #08c, - yellow-1: #fff1ba, - yellow-2: #fff4cc, - red-1: #dd4b39, - red-2: #eea59c, - black-search: #2a2a2e, - white-search: #f9f9fa, - grey-search: #b1b1b3, - blue-menu: #f4fcff, - ), -); - -$theme-map: null; - -@mixin themify($themes: $themes) { - @each $theme, $map in $themes { - .theme-#{$theme} & { - $theme-map: () !global; - @each $key, $submap in $map { - $value: map-get(map-get($themes, $theme), "#{$key}"); - $theme-map: map-merge( - $theme-map, - ( - $key: $value, - ) - ) !global; - } - @content; - $theme-map: null !global; - } - } -} - -@function themed($key) { - @return map-get($theme-map, $key); -} - -// Shared -@mixin hover-black { - &:hover { - svg { - @include themify($themes) { - fill: themed("black-1"); - } - } - } -} - -@mixin icon-special($size, $color) { - svg { - vertical-align: middle; - fill: $color; - height: $size; - width: $size; - } -} +@import "tokens"; // Classes .button { margin: 10px; - padding: 20px; - border-radius: 2px; + padding: 14px; + border-radius: var(--radius-row); position: relative; text-align: center; - font-size: 16px; + font-size: 15px; + font-weight: 600; + font-family: var(--font-sans); width: -moz-available; width: -webkit-fill-available; - @include themify($themes) { - background: themed("white-1"); - border: themed("grey-2") 1px solid; - color: themed("grey-1"); - } + background: var(--row); + border: var(--border) 1px solid; + color: var(--text); cursor: pointer; &:hover { - @include themify($themes) { - color: themed("black-1"); - } + background: var(--row-hover); + color: var(--text); } } .button-small { @extend .button; - font-size: 12px; - margin: 20px 100px; - padding: 10px; + font-size: 13px; + margin: 16px 0; + padding: 11px; } .input { display: block; margin: 15px 10px 20px 10px; - padding: 5px 10px; + padding: 11px 13px; width: 260px; - border: none; - @include themify($themes) { - color: themed("black-1"); - border-bottom: themed("black-1") 1px solid; - background: themed("grey-3"); - } + border: var(--border-strong) 1px solid; + border-radius: var(--radius-row); + font-family: var(--font-sans); + color: var(--text); + background: var(--row); outline: none; + + &:focus { + border-color: var(--accent); + background: var(--app-bg); + } } a { - @include themify($themes) { - color: themed("blue-1"); - } + color: var(--accent); } diff --git a/sass/import.scss b/sass/import.scss index 798beefd6..464676a4c 100644 --- a/sass/import.scss +++ b/sass/import.scss @@ -1,75 +1,143 @@ @import "ui"; -[v-cloak] { - display: none; +* { + box-sizing: border-box; } -* { - font-family: arial, "Microsoft YaHei"; +html, +body { + margin: 0; +} + +body { + font-family: var(--font-sans); +} + +[v-cloak] { + display: none; } p { - font-size: 16px; + font-size: 15px; } #import { - width: 900px; - position: relative; - margin: 0 auto; + min-height: 100vh; + background: var(--canvas); + color: var(--text); + padding: 48px 24px; } -#import_info { - margin: 10px 20px 20px 20px; +.import-card { + max-width: 760px; + margin: 0 auto; + background: var(--app-bg); + border: 1px solid var(--border); + border-radius: var(--radius-card); + box-shadow: var(--shadow); + overflow: hidden; } -.import_tab { - text-align: center; - font-size: 0; +.import-header { + display: flex; + align-items: center; + gap: 13px; + padding: 34px 44px 0; - input { - display: none; + .import-logo { + width: 32px; + height: 32px; + border-radius: 10px; + background: var(--accent); + color: var(--accent-fg); + display: flex; + align-items: center; + justify-content: center; + flex: none; - &:checked + label { - background: #eee; + svg { + width: 18px; + height: 18px; } } - label { - width: 250px; - height: 50px; - font-size: 18px; - text-align: center; - display: inline-grid; - align-items: center; - margin: 20px; - cursor: pointer; - border-radius: 2px; + h1 { + font-size: 24px; + font-weight: 800; + letter-spacing: -0.02em; + margin: 0; + } +} - &:hover { - background: #eee; - } +.import-desc { + padding: 8px 44px 0; + font-size: 14px; + line-height: 1.55; + color: var(--text-dim); + max-width: 620px; +} + +// Interactive underline tabs +.import-tabs { + display: flex; + gap: 4px; + padding: 24px 44px 0; + border-bottom: 1px solid var(--border); +} + +.import-tab { + display: flex; + align-items: center; + gap: 9px; + margin-bottom: -1px; + padding: 13px 20px; + border: none; + border-bottom: 2.5px solid transparent; + background: transparent; + color: var(--text-dim); + font-family: var(--font-sans); + font-size: 14px; + font-weight: 700; + cursor: pointer; + + svg { + width: 17px; + height: 17px; + } + + &:hover { + color: var(--text); + } + + &.active { + color: var(--accent); + border-bottom-color: var(--accent); } } -button, -.import_file label { - display: inline-grid; - width: 260px !important; - height: 60px; - border: #ccc 1px solid; - background: white; - border-radius: 2px; - position: relative; - text-align: center; +.import-panel { + padding: 30px 44px 38px; +} + +.import-panel button { + display: inline-flex; align-items: center; - font-size: 16px; - color: gray; + justify-content: center; + min-width: 200px; + height: 46px; + padding: 0 22px; + border: none; + border-radius: var(--radius-row); + background: var(--accent); + color: var(--accent-fg); + font-family: var(--font-sans); + font-size: 14px; + font-weight: 700; cursor: pointer; outline: none; - margin-left: 0px !important; &:hover { - color: black; + filter: brightness(1.06); } } @@ -79,45 +147,278 @@ button, input { display: none; } + + label { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 8px; + padding: 44px 20px; + border: 2px dashed var(--border-strong); + border-radius: 18px; + background: var(--row); + color: var(--text-dim); + font-family: var(--font-sans); + font-size: 15px; + font-weight: 700; + cursor: pointer; + + &:hover, + &.dragover { + border-color: var(--accent); + } + } +} + +// File import panel (design "18 IMPORT" — file tab) +.file-import { + .dropzone { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 16px; + padding: 46px 20px; + border: 2px dashed var(--border-strong); + border-radius: 18px; + background: var(--row); + text-align: center; + cursor: pointer; + + &:hover, + &.dragover { + border-color: var(--accent); + } + + input { + display: none; + } + } + + .dropzone-icon { + width: 58px; + height: 58px; + border-radius: 16px; + background: var(--accent-soft); + color: var(--accent); + display: flex; + align-items: center; + justify-content: center; + + svg { + width: 28px; + height: 28px; + } + } + + .dropzone-title { + font-size: 16px; + font-weight: 700; + margin-bottom: 4px; + } + + .dropzone-hint { + font-size: 13px; + color: var(--text-faint); + } + + .dropzone-btn { + padding: 11px 22px; + border-radius: 12px; + background: var(--accent); + color: var(--accent-fg); + font-size: 13.5px; + font-weight: 700; + } + + .import-note { + display: flex; + align-items: center; + gap: 11px; + margin-top: 18px; + padding: 14px 16px; + border-radius: 13px; + background: var(--row); + color: var(--text-dim); + + svg { + flex: none; + width: 18px; + height: 18px; + } + + div { + flex: 1; + font-size: 13px; + } + } + + .file-card { + display: flex; + align-items: center; + gap: 13px; + padding: 16px; + border-radius: 14px; + background: var(--row); + border: 1px solid var(--border); + margin-bottom: 20px; + } + + .file-card-icon { + width: 42px; + height: 42px; + border-radius: 11px; + flex: none; + background: var(--accent-soft); + color: var(--accent); + display: flex; + align-items: center; + justify-content: center; + + svg { + width: 21px; + height: 21px; + } + } + + .file-card-text { + flex: 1; + min-width: 0; + } + + .file-card-name { + font-size: 14px; + font-weight: 700; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + .file-card-badge { + display: flex; + align-items: center; + gap: 5px; + font-size: 12px; + font-weight: 600; + color: var(--warn); + + svg { + width: 12px; + height: 12px; + flex: none; + } + } + + .file-card-remove { + width: 30px; + height: 30px; + border-radius: 9px; + flex: none; + background: var(--app-bg); + color: var(--text-dim); + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + + svg { + width: 15px; + height: 15px; + } + + &:hover { + color: var(--text); + } + } + + .pass-hint { + font-size: 12px; + color: var(--text-faint); + margin-top: 8px; + } + + .pass-submit { + margin-top: 18px; + } +} + +.field-label { + display: block; + font-size: 12px; + font-weight: 700; + color: var(--text-dim); + margin-bottom: 8px; +} + +.pass-input { + width: 100%; + padding: 14px; + border-radius: 13px; + border: 1px solid var(--accent); + background: var(--app-bg); + color: var(--text); + font-family: var(--font-mono); + font-size: 15px; + letter-spacing: 2px; + outline: none; +} + +.import_code_passphrase { + margin-bottom: 18px; } .import_encrypted { - margin-bottom: 20px; + margin-bottom: 18px; input { margin-left: 0; + accent-color: var(--accent); } } .import_code { - float: left; - margin-left: 30px; - margin-right: 40px; - textarea { - width: 250px; - height: 400px; - padding: 10px; + width: 100%; + height: 320px; + padding: 12px; + border: 1px solid var(--border-strong); + border-radius: var(--radius-row); + background: var(--row); + color: var(--text); + font-family: var(--font-mono); + font-size: 13px; outline: none; resize: none; - box-sizing: border-box; + + &:focus { + border-color: var(--accent); + background: var(--app-bg); + } } } .error_password { - font-size: 18px; - color: gray; + font-size: 16px; + color: var(--text-dim); text-align: center; } .import_passphrase input, .import_file_passphrase_input input { - padding: 10px; - margin-bottom: 20px; - width: 250px; - border: #ccc 1px solid; - background: white; + padding: 11px 13px; + margin-bottom: 16px; + width: 100%; + border: 1px solid var(--border-strong); + border-radius: var(--radius-row); + background: var(--row); + color: var(--text); outline: none; + + &:focus { + border-color: var(--accent); + background: var(--app-bg); + } } .import_file_passphrase { @@ -126,6 +427,6 @@ button, } .import_file_passphrase_input { - display: inline-grid; + display: grid; grid-template-rows: min-content min-content; } diff --git a/sass/options.scss b/sass/options.scss new file mode 100644 index 000000000..55f545523 --- /dev/null +++ b/sass/options.scss @@ -0,0 +1,69 @@ +@import "ui"; + +* { + box-sizing: border-box; +} + +html, +body { + margin: 0; +} + +body { + font-family: var(--font-sans); +} + +#options { + min-height: 100vh; + background: var(--canvas); + color: var(--text); + padding: 48px 24px; + + .options-card { + max-width: 480px; + margin: 0 auto; + background: var(--app-bg); + border: var(--border) 1px solid; + border-radius: var(--radius-card); + box-shadow: var(--shadow); + padding: 32px 28px; + } + + h2 { + font-size: 20px; + font-weight: 800; + letter-spacing: -0.01em; + margin: 0 0 10px; + } + + p { + font-size: 14px; + line-height: 1.55; + color: var(--text-dim); + margin: 0 0 16px; + } + + label { + font-size: 14px; + color: var(--text); + } + + button { + margin-top: 18px; + padding: 13px 18px; + border: none; + border-radius: var(--radius-row); + background: var(--danger); + color: #fff; + font-family: var(--font-sans); + font-size: 15px; + font-weight: 700; + cursor: pointer; + + &:disabled { + background: var(--row); + color: var(--text-faint); + cursor: default; + } + } +} diff --git a/sass/permissions.scss b/sass/permissions.scss index 0bef0c1bf..789d8c5ed 100644 --- a/sass/permissions.scss +++ b/sass/permissions.scss @@ -1,43 +1,139 @@ @import "ui"; -[v-cloak] { - display: none; +* { + box-sizing: border-box; } -* { - font-family: arial, "Microsoft YaHei"; +html, +body { + margin: 0; } -p { - font-size: 16px; +body { + font-family: var(--font-sans); +} + +[v-cloak] { + display: none; } #permissions { - width: 900px; - position: relative; + min-height: 100vh; + background: var(--canvas); + color: var(--text); + padding: 48px 24px; +} + +.perm-card { + max-width: 560px; margin: 0 auto; + background: var(--app-bg); + border: 1px solid var(--border); + border-radius: var(--radius-card); + box-shadow: var(--shadow); + padding: 32px 28px; } -h2 { - margin-top: 3em; +.perm-head { + display: flex; + align-items: center; + gap: 14px; + margin-bottom: 22px; + + .perm-icon { + width: 52px; + height: 52px; + border-radius: 16px; + flex: none; + background: var(--accent-soft); + color: var(--accent); + display: flex; + align-items: center; + justify-content: center; + + svg { + width: 26px; + height: 26px; + } + } + + h1 { + font-size: 22px; + font-weight: 800; + letter-spacing: -0.02em; + margin: 0; + } } -button { - display: inline-grid; - padding: 10px 20px; - border: #ccc 1px solid; - background: white; - border-radius: 2px; - position: relative; - text-align: center; +.perm-toggle { + display: flex; align-items: center; - font-size: 16px; - color: gray; + gap: 9px; + font-size: 13.5px; + color: var(--text-dim); + margin-bottom: 18px; cursor: pointer; - outline: none; - margin-left: 0px !important; - &:not(:disabled):hover { - color: black; + input { + accent-color: var(--accent); + } +} + +.perm-list { + display: flex; + flex-direction: column; + gap: 10px; +} + +.perm-item { + display: flex; + align-items: center; + gap: 14px; + padding: 14px 16px; + border-radius: var(--radius-row); + background: var(--row); + border: 1px solid var(--border); + + .perm-item-text { + flex: 1; + min-width: 0; + } + + .perm-item-id { + font-size: 15px; + font-weight: 700; + color: var(--text); + margin-bottom: 3px; + } + + p { + font-size: 13px; + line-height: 1.5; + color: var(--text-dim); + margin: 0; + } + + .perm-required { + color: var(--text-faint); + font-style: italic; + margin-top: 4px; + } + + button { + flex: none; + padding: 9px 16px; + border: 1px solid var(--border-strong); + border-radius: var(--radius-row); + background: var(--app-bg); + color: var(--text); + font-family: var(--font-sans); + font-size: 13px; + font-weight: 600; + cursor: pointer; + + &:hover { + border-color: var(--danger); + color: var(--danger); + } } } diff --git a/sass/popup.scss b/sass/popup.scss index 6849dd8c3..1a9aa4c94 100644 --- a/sass/popup.scss +++ b/sass/popup.scss @@ -1,6 +1,9 @@ @import "ui"; -// Structure +// Layout constants +$header-h: 56px; +$popup-w: 360px; +$popup-h: 580px; * { margin: 0; @@ -13,12 +16,14 @@ } body { - width: 320px; - height: 480px; + width: $popup-w; + height: $popup-h; transform-origin: left top; overflow: hidden; - font-family: arial, "Microsoft YaHei"; + font-family: var(--font-sans); font-size: 16px; + color: var(--text); + background: var(--app-bg); cursor: default; user-select: none; @@ -36,13 +41,12 @@ svg { } .icon { - @include themify($themes) { - fill: themed("grey-1"); - } + fill: var(--text-dim); vertical-align: middle; + svg { - height: 16px; - width: 16px; + height: 17px; + width: 17px; } } @@ -51,210 +55,288 @@ svg { top: -1000px; } -// Header +// ── Header ──────────────────────────────────────────────────────────── .header { - height: 38px; - line-height: 38px; + height: $header-h; + display: flex; + align-items: center; + gap: 11px; + padding: 0 16px; + background: var(--header); + border-bottom: 1px solid var(--border); position: relative; - text-align: center; - font-size: 16px; - @include themify($themes) { - color: themed("black-1"); - background: themed("white-1"); - border-bottom: themed("grey-2") 1px solid; + + .brand { + display: flex; + align-items: center; + gap: 10px; + flex: 1; + min-width: 0; } - .icon { - @include hover-black; - cursor: pointer; + .brand-logo { + width: 28px; + height: 28px; + border-radius: 8px; + background: var(--accent); + color: var(--accent-fg); + display: flex; + align-items: center; + justify-content: center; + flex: none; svg { - vertical-align: sub; + width: 20px; + height: 20px; } } -} -#i-menu { - position: absolute; - left: 20px; - bottom: 0; -} + .brand-name { + font-size: 16px; + font-weight: 700; + letter-spacing: -0.01em; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } -#i-lock { - position: absolute; - left: 45px; - bottom: 0; -} + // MenuPage reuses .header with a title + back button + #menuName { + font-size: 16px; + font-weight: 700; + flex: 1; + } -#i-sync { - position: absolute; - left: 70px; - bottom: 0; - cursor: default; - @include themify($themes) { - fill: themed("grey-2"); + .header-actions { + display: flex; + align-items: center; + gap: 7px; + flex: none; } - &:hover { - svg { - fill: inherit; + .icon { + width: 34px; + height: 34px; + border-radius: 10px; + background: var(--row); + color: var(--text-dim); + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + transition: background 0.15s, color 0.15s; + + &:hover { + background: var(--row-hover); + svg { + fill: var(--text); + } } } -} -#i-plus, -#i-qr { - position: absolute; - right: 45px; - bottom: 0; -} + #i-plus { + background: var(--accent); + svg { + fill: var(--accent-fg); + } + &:hover { + filter: brightness(1.06); + svg { + fill: var(--accent-fg); + } + } + } -#i-edit { - position: absolute; - right: 20px; - bottom: 0; -} + #i-sync { + background: transparent; + cursor: default; + svg { + fill: var(--text-faint); + } + &:hover { + background: transparent; + } + } -#i-close { - position: absolute; - left: 20px; - bottom: 0; + #i-close { + background: transparent; + &:hover { + background: var(--row); + } + } } -// Search & Filter +// ── Search & filter (under header) ────────────────────────────────────── .under-header { - padding: 0 10px; - margin-left: 10px; - font-size: 12px; - height: 24px; - line-height: 24px; - cursor: pointer; display: none; } #filter { - @include themify($themes) { - background: themed("yellow-2"); - } + display: flex; + align-items: center; + gap: 9px; + padding: 11px 14px; + border-radius: 12px; + background: var(--accent-soft); + cursor: pointer; &:hover { - @include themify($themes) { - background: themed("yellow-1"); - } + filter: brightness(0.98); + } + + .filter-globe { + width: 15px; + height: 15px; + flex: none; + color: var(--accent); + } + + .filter-text { + flex: 1; + min-width: 0; + display: flex; + flex-direction: column; + color: var(--accent); + } + + .filter-label { + font-size: 12.5px; + font-weight: 600; + } + + .filter-domain { + font-size: 13px; + font-weight: 700; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + .filter-showall { + flex: none; + font-size: 12px; + font-weight: 700; + color: var(--accent); + text-decoration: underline; } } #search { position: relative; - @include themify($themes) { - background: themed("white-1"); - border: themed("grey-2") 1px solid; - border-top: none; - } + display: flex; + align-items: center; + gap: 9px; + padding: 10px 13px; + border-radius: 12px; + background: var(--row); + border: 1px solid var(--border); - &:hover { - @include themify($themes) { - background: themed("white-search"); - } + &:focus-within { + border-color: var(--accent); + background: var(--app-bg); } input { - font-family: arial, "Microsoft YaHei"; + flex: 1; + min-width: 0; + font-family: var(--font-sans); + font-size: 14px; background: none; border: none; - width: 100%; - height: 100%; - @include themify($themes) { - color: themed("black-search"); - } + outline: none; + color: var(--text); } #searchHint { - position: absolute; - top: -1px; - right: 0px; - padding-right: 10px; - display: grid; - grid-template-rows: 4.5px 15px 4.5px; - grid-template-columns: 15px; + flex: none; + color: var(--text-faint); } #searchHintBorder { - @include themify($themes) { - color: themed("white-1"); - background: themed("grey-search"); - } - text-align: center; - border-radius: 1.5px; + min-width: 16px; + padding: 0 5px; + border-radius: 4px; + background: var(--border); + color: var(--text-faint); + font-size: 11px; line-height: 16px; - font-weight: bolder; + text-align: center; + font-weight: 700; } } -// Codes +// ── Code list ─────────────────────────────────────────────────────────── #codes { - height: 442px; + height: $popup-h - $header-h; overflow-x: hidden; - overflow-y: hidden; - @include themify($themes) { - background: themed("grey-background"); - } - padding-right: 10px; + overflow-y: auto; + background: var(--app-bg); + padding: 10px 12px; + display: flex; + flex-direction: column; + gap: 8px; .deleteAction { - @include themify($themes) { - @include icon-special(20px, themed("red-1")); + flex: none; + width: 24px; + height: 24px; + border-radius: var(--radius-pill); + display: none; // shown in edit mode + align-items: center; + justify-content: center; + fill: var(--danger); + cursor: pointer; + + &:hover { + background: var(--app-bg); } - position: absolute; - top: -10px; - left: -10px; - z-index: 10; - display: none; - } - &:hover { - padding-right: 0; - overflow-y: scroll; + svg { + width: 20px; + height: 20px; + } } &.edit { - .code { - @include themify($themes) { - color: themed("grey-2") !important; - } - user-select: none; - cursor: default; - } - .issuer, - .showqr, - .showqr.hidden, - .pin, + .account, + .code, + .entry-actions, + .timebar, + .remaining-badge, .no-entry { display: none; } - .issuerEdit, - .movehandle, - #add { + .issuerEdit { display: block; } + .movehandle, .deleteAction { - display: block; - cursor: pointer; + display: flex; + } + + .entry { + cursor: default; + padding: 10px 11px; + } + + .monogram { + width: 34px; + height: 34px; + font-size: 15px; + border-radius: 10px; } - .sector, - .counter { - position: absolute; - left: -1000px; - opacity: 0; + .entry-text { + display: flex; + flex-direction: column; + gap: 4px; } } - &.filter .entry[filtered], - &.search .entry[notSearched] { + &.search .entry.notSearched { height: 0; margin: 0; padding: 0; @@ -264,218 +346,204 @@ svg { position: absolute; } - &.filter #filter, &.search #search { - display: block; + display: flex; } +} - &:not(.edit) { - // Is this used? - .entry[unencrypted="true"]:hover .warning { - height: 24px; - } - - .code.timeout:not(.hotp) { - animation: twinkling 1s infinite ease-in-out; - } - } +// vuedraggable wraps the rows in its own element, so the gap must live here +// (a gap on #codes only spaces the search/filter/list groups, not the rows). +.entries { + display: flex; + flex-direction: column; + gap: 8px; +} - .no-entry { - @include themify($themes) { - color: themed("grey-1"); - } +// ── Entry row ─────────────────────────────────────────────────────────── +.entry { + display: flex; + align-items: center; + gap: 12px; + padding: 11px 13px 14px; + border-radius: var(--radius-row); + background: var(--row); + border: 1px solid transparent; + position: relative; + overflow: hidden; + cursor: pointer; + transition: background 0.15s, border-color 0.15s; - margin: 20px; - text-align: center; + &:hover { + background: var(--row-hover); + border-color: var(--border); - svg { - width: 48px; - height: 48px; - margin: 20px; + .movehandle svg { + fill: var(--text); } } -} -.entry { - margin: 10px; - margin-right: 0; - padding: 10px; - @include themify($themes) { - border: themed("grey-2") 1px solid; - background: themed("white-1"); - } - border-radius: 2px; - position: relative; - display: block; - cursor: pointer; + .monogram { + width: 38px; + height: 38px; + border-radius: 11px; + flex: none; + display: flex; + align-items: center; + justify-content: center; + font-weight: 800; + font-size: 17px; + } + + .entry-text { + flex: 1; + min-width: 0; + } .issuer { - font-size: 12px; - @include themify($themes) { - color: themed("black-1"); - } - width: 80%; + font-size: 14px; + font-weight: 700; + color: var(--text); + white-space: nowrap; + overflow: hidden; text-overflow: ellipsis; + } + + .account { + font-size: 12px; + color: var(--text-faint); + white-space: nowrap; overflow: hidden; + text-overflow: ellipsis; } .code { - font-size: 36px; - @include themify($themes) { - color: themed("blue-1"); - } - width: 80%; + font-family: var(--font-mono); + font-size: 21px; + font-weight: 600; + letter-spacing: 0.5px; + color: var(--accent); + font-variant-numeric: tabular-nums; user-select: text; - font-family: "Droid Sans Mono"; - } + white-space: nowrap; - .sector, - .counter { - width: 20px; - height: 20px; - position: absolute; - right: 10px; - bottom: 10px; + &.timeout:not(.hotp) { + animation: glint 1s infinite ease-in-out; + } } - .sector { - svg { - width: 16px; - height: 16px; - margin: 2px; - } + .issuerEdit { + display: none; // shown in edit mode - circle { - fill: none; - transform: rotate(-90deg); - transform-origin: 50% 50%; - @include themify($themes) { - stroke: themed("grey-1"); + input { + width: 100%; + padding: 6px 8px; + border-radius: 8px; + border: 1px solid var(--border); + background: var(--app-bg); + font-family: var(--font-sans); + font-size: 13px; + color: var(--text); + outline: none; + + &:focus { + border-color: var(--accent); } - stroke-width: 8px; - stroke-dasharray: 25.12; - animation-name: timer; - animation-iteration-count: infinite; - animation-timing-function: linear; } + + &.issuerEdit-issuer input { + font-weight: 700; + } + + &.issuerEdit-account input { + font-size: 12px; + color: var(--text-dim); + } + } + + // Right-side actions (QR / HOTP refresh always visible; pin on hover) + .entry-actions { + display: flex; + align-items: center; + gap: 4px; + flex: none; } .counter { - @include themify($themes) { - @include icon-special(18px, themed("grey-1")); - } - text-align: center; + width: 32px; + height: 32px; + border-radius: 9px; + flex: none; + display: flex; + align-items: center; + justify-content: center; cursor: pointer; + fill: var(--text-faint); + background: transparent; - .disabled { - svg { - @include themify($themes) { - fill: themed("grey-2"); - } - } - cursor: default; + svg { + width: 17px; + height: 17px; } - &:not(.disabled):hover { + &:hover { + background: var(--app-bg); svg { - @include themify($themes) { - fill: themed("black-1"); - } + fill: var(--accent); } } } - .issuerEdit { - display: none; - - input { - border: none; - height: 14px; - width: 70%; - font-size: 12px; - outline: none; - @include themify($themes) { - background: themed("grey-3"); - } + .counter.disabled { + cursor: default; + svg { + fill: var(--border-strong); } } - .movehandle { - @include themify($themes) { - @include icon-special(24px, themed("grey-2")); - } - height: 98px; - line-height: 98px; - right: 10px; - top: 0; + // Countdown: depleting bar along the bottom edge + seconds badge + .timebar { position: absolute; - cursor: move; - display: none; + left: 0; + right: 0; + bottom: 0; + height: 3px; + background: var(--ring-track); } - .showqr, - .pin { - @include themify($themes) { - @include icon-special(20px, themed("grey-2")); - } - @include hover-black; - right: 10px; - top: 10px; - position: absolute; - cursor: pointer; - opacity: 0; + .timebar-fill { + height: 100%; + border-radius: 0 3px 3px 0; + transition: width 1s linear, background 0.4s ease; } - .showqr { - right: 35px; + .remaining-badge { + position: absolute; + right: 13px; + bottom: 8px; + font-family: var(--font-mono); + font-size: 9.5px; + font-weight: 600; + color: var(--text-faint); + font-variant-numeric: tabular-nums; + line-height: 1; } - &:hover { - .showqr, - .pin { - opacity: 1; - } + .movehandle { + flex: none; + display: none; // shown in edit mode + align-items: center; + padding: 4px; + fill: var(--text-faint); + cursor: grab; - .movehandle { - svg { - @include themify($themes) { - fill: themed("black-1"); - } - } + svg { + width: 18px; + height: 18px; } } - /* - // Is this used? - &[dropOver="true"] { - border: $grey-1 1px dashed; - } - - // Is this used? - &[unencrypted="true"] .warning { - position: absolute; - height: 0; - line-height: 12px; - font-size: 12px; - padding: 0 10px; - margin: 0 4px; - width: 250px; - bottom: 4px; - left: 0; - background: #EC6959; - color: $white; - cursor: pointer; - overflow: hidden; - border-radius: 2px; - transition: height 0.2s; - } - */ } .pinnedEntry { - .pin { - opacity: 1; - } - .movehandle { display: none !important; } @@ -485,100 +553,213 @@ svg { cursor: default; } -#add { - @extend .button; - @include hover-black; - margin-right: 0; - line-height: 56px; - display: none; +// Smart-filter view (design "12 FILTERED TO SITE") +.entry.matchedEntry { + border-color: var(--accent); } -// Modals -#notification { - position: absolute; - left: 60px; - top: -1000px; - width: 200px; - height: 60px; - line-height: 60px; - text-align: center; - @include themify($themes) { - background: themed("black-transparent"); - color: themed("white-1"); - } - font-size: 20px; - border-radius: 2px; - - &.fadein { - top: 190px; - animation: fadeshow 0.2s 1 ease-out; - } +.entry.dimmed { + opacity: 0.5; +} - &.fadeout { - top: 190px; - animation: fadehide 0.2s 1 ease-in; - } +.other-accounts { + font-size: 11px; + font-weight: 700; + color: var(--text-faint); + letter-spacing: 0.06em; + text-transform: uppercase; + padding: 14px 4px 6px; } -.message-box { - position: absolute; - width: 300px; - padding: 10px; - @include themify($themes) { - color: themed("black-1"); - border: themed("grey-1") 1px solid; - background: themed("white-1"); - box-shadow: 1px 1px 3px themed("grey-1"); - } - border-radius: 2px; - left: 10px; - top: 150px; - z-index: 1000; +// Right-click context menu on an entry +.entry-ctx-backdrop { + position: fixed; + inset: 0; + z-index: 1150; +} - .button-small { - margin-top: 10px; - margin-bottom: 10px; - } +.entry-ctx { + position: fixed; + z-index: 1200; + min-width: 160px; + padding: 6px; + background: var(--app-bg); + border: 1px solid var(--border); + border-radius: 12px; + box-shadow: var(--shadow); + cursor: default; } -.buttons { +.entry-ctx-item { display: flex; - justify-content: center; - text-align: center; + align-items: center; + gap: 10px; + padding: 9px 11px; + border-radius: 8px; + font-size: 13.5px; + font-weight: 600; + color: var(--text); + fill: var(--text-dim); + cursor: pointer; - .button-small { - display: inline-block; - flex-basis: content; - margin: 10px; - padding: 5px 20px; + svg { + width: 17px; + height: 17px; } -} -#qr { - width: 320px; - height: 480px; - top: -1000px; - left: 0; + &:hover { + background: var(--row); + } +} + +// Edit-mode "Add account" row (design "10 EDIT MODE") +.edit-add { + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + padding: 14px; + border-radius: 14px; + border: 2px dashed var(--border-strong); + color: var(--text-dim); + fill: none; + stroke: currentColor; + font-size: 13.5px; + font-weight: 700; + cursor: pointer; + + svg { + width: 17px; + height: 17px; + } + + &:hover { + border-color: var(--accent); + color: var(--accent); + } +} + +// ── Empty state ───────────────────────────────────────────────────────── +.no-entry { + margin: auto; + text-align: center; + display: flex; + flex-direction: column; + align-items: center; + padding: 30px 24px; + color: var(--text-dim); + + .no-entry-icon { + width: 64px; + height: 64px; + border-radius: 18px; + border: 2px dashed var(--border-strong); + color: var(--text-faint); + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 18px; + cursor: pointer; + + &:hover { + border-color: var(--accent); + color: var(--accent); + } + + svg { + width: 28px; + height: 28px; + } + } + + .no-entry-title { + font-size: 17px; + font-weight: 700; + color: var(--text); + margin-bottom: 6px; + } + + p { + font-size: 13px; + margin-bottom: 20px; + } + + .no-entry-add { + padding: 13px 22px; + border: none; + border-radius: 13px; + background: var(--accent); + color: var(--accent-fg); + font-family: var(--font-sans); + font-size: 14px; + font-weight: 700; + cursor: pointer; + + &:hover { + filter: brightness(1.06); + } + } +} + +// ── Copy toast ────────────────────────────────────────────────────────── +#notification { position: absolute; - z-index: 10; - @include themify($themes) { - background-color: themed("white-transparent"); + left: 50%; + bottom: 20px; + transform: translateX(-50%); + max-width: calc(100% - 40px); + padding: 11px 18px; + border-radius: var(--radius-pill); + background: var(--text); + color: var(--app-bg); + font-size: 13px; + font-weight: 700; + box-shadow: 0 8px 22px rgba(0, 0, 0, 0.22); + white-space: nowrap; + opacity: 0; + pointer-events: none; + z-index: 1100; + + &.fadein { + opacity: 1; + animation: toastUp 0.22s ease; } - background-repeat: no-repeat; - background-position: center; - canvas { - display: none; + &.fadeout { + opacity: 0; + animation: fadehide 0.2s ease; } +} - &.qrfadein { - top: 0; - animation: fadeshow 0.2s 1 ease-out; +// ── Alert / confirm dialog ────────────────────────────────────────────── +.message-box { + position: absolute; + width: calc(100% - 36px); + left: 18px; + top: 150px; + padding: 20px; + border-radius: var(--radius-card); + color: var(--text); + background: var(--app-bg); + border: 1px solid var(--border); + box-shadow: var(--shadow); + z-index: 1000; + + .button-small { + margin: 14px auto 0; } +} - &.qrfadeout { - top: 0; - animation: fadehide 0.2s 1 ease-in; +.buttons { + display: flex; + justify-content: center; + gap: 10px; + + .button-small { + margin: 14px 0 0; + padding: 9px 22px; + flex: 0 0 auto; + width: auto; } } @@ -588,125 +769,278 @@ svg { height: 100%; top: 0; left: 0; + background: var(--overlay); z-index: 900; } -// Info -#info { +// ── QR overlay ────────────────────────────────────────────────────────── +// QR bottom sheet (design "04 LIVE LIST · interactive") +#qr { position: absolute; - height: 460px; - width: 300px; - padding: 10px; - @include themify($themes) { - color: themed("black-1"); - border: themed("grey-1"); - background: themed("white-1"); - box-shadow: 1px 1px 3px themed("grey-1"); + width: $popup-w; + height: $popup-h; + top: -1000px; + left: 0; + z-index: 10; + background: rgba(10, 10, 12, 0.45); + display: flex; + align-items: flex-end; + + &.qrfadein { + top: 0; + animation: fadeshow 0.18s 1 ease-out; } + + &.qrfadeout { + top: 0; + animation: fadehide 0.18s 1 ease-in; + } + + .qr-sheet { + width: 100%; + padding: 24px 22px 26px; + background: var(--app-bg); + border-radius: 22px; + box-shadow: 0 -10px 40px rgba(0, 0, 0, 0.25); + display: flex; + flex-direction: column; + align-items: center; + animation: sheetUp 0.22s ease; + } + + .qr-head { + display: flex; + align-items: center; + gap: 11px; + width: 100%; + margin-bottom: 20px; + } + + .qr-mono { + width: 38px; + height: 38px; + border-radius: 11px; + flex: none; + display: flex; + align-items: center; + justify-content: center; + font-weight: 800; + font-size: 17px; + } + + .qr-headtext { + flex: 1; + min-width: 0; + } + + .qr-issuer { + font-size: 15px; + font-weight: 700; + color: var(--text); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + .qr-account { + font-size: 12px; + color: var(--text-faint); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + .qr-close { + width: 32px; + height: 32px; + border-radius: 9px; + flex: none; + border: none; + background: var(--row); + color: var(--text-dim); + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + + svg { + width: 16px; + height: 16px; + } + + &:hover { + background: var(--row-hover); + color: var(--text); + } + } + + .qr-img { + padding: 16px; + background: #fff; + border-radius: 18px; + box-shadow: 0 4px 18px rgba(0, 0, 0, 0.1); + margin-bottom: 16px; + + img { + display: block; + width: 180px; + height: 180px; + image-rendering: pixelated; + } + } + + .qr-caption-title { + font-size: 13px; + font-weight: 700; + color: var(--text); + margin-bottom: 4px; + } + + .qr-caption { + font-size: 12.5px; + line-height: 1.5; + color: var(--text-dim); + text-align: center; + max-width: 250px; + } +} + +// ── Modal pane (#info) ────────────────────────────────────────────────── +#info { + position: absolute; + height: $popup-h - 20px; + width: calc(100% - 20px); left: 10px; top: -1000px; + padding: 0; + border-radius: var(--radius-card); + color: var(--text); + background: var(--app-bg); + border: 1px solid var(--border); + box-shadow: var(--shadow); z-index: 100; + overflow: hidden; #infoContent { - height: 420px; + height: 100%; + padding: 18px; overflow-y: auto; overflow-x: hidden; p { - margin-bottom: 20px; + margin-bottom: 16px; } } #infoClose { - @include hover-black; - @include themify($themes) { - @include icon-special(14px, themed("grey-1")); - } - height: 20px; - width: 20px; + position: absolute; + top: 14px; + right: 14px; + z-index: 2; + width: 28px; + height: 28px; + border-radius: 9px; + display: flex; + align-items: center; + justify-content: center; cursor: pointer; + fill: var(--text-dim); + + &:hover { + background: var(--row); + svg { + fill: var(--text); + } + } + + svg { + width: 16px; + height: 16px; + } } label { display: block; - margin: 10px 0 0 10px; + margin: 10px 0 0 4px; + font-size: 13px; } .control-group { display: flex; justify-content: space-between; align-items: center; + padding: 4px 0; } .control-group select { - width: 100px; - padding: 5px 10px; - @include themify($themes) { - border: themed("grey-2") 1px solid; - background: themed("white-1"); - color: themed("black-1"); - } + width: 130px; + padding: 9px 12px; + border-radius: var(--radius-row); + border: 1px solid var(--border-strong); + background: var(--row); + color: var(--text); + font-family: var(--font-sans); } + // Pill toggle .control-group input[type="checkbox"] { position: relative; border: none; background: none; - width: 34px; - height: 16px; + width: 46px; + height: 27px; -webkit-appearance: none; -moz-appearance: none; appearance: none; outline: none; + cursor: pointer; } .control-group input[type="checkbox"]::before { content: ""; display: block; - width: 28px; - height: 12px; - border-radius: 8px; - background: grey; - margin: 2px 3px; - transition: all 80ms linear; + width: 46px; + height: 27px; + border-radius: var(--radius-pill); + background: var(--border-strong); + transition: background 0.2s linear; } .control-group input[type="checkbox"]::after { content: ""; display: block; position: absolute; - width: 16px; - height: 16px; - border-radius: 10px; - background: white; - top: 0; - left: 0; - box-shadow: 0 0 2px black; - transition: all 80ms linear; + width: 23px; + height: 23px; + border-radius: var(--radius-pill); + background: #fff; + top: 2px; + left: 2px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); + transition: transform 0.2s linear; } .control-group input[type="checkbox"]:checked::before { - background: #08c; + background: var(--accent); } .control-group input[type="checkbox"]:checked::after { - transform: matrix(1, 0, 0, 1, 18, 0); + transform: translateX(19px); } .combo-label { display: flex; - margin: 20px; - margin-right: 0px; - font-size: 16px; + align-items: center; + margin: 16px 4px; + font-size: 14px; } .checkbox { - margin: 20px; + margin: 16px 4px; } select { - margin: 20px; - font-size: 12px; + font-size: 13px; } a { @@ -719,13 +1053,11 @@ svg { .text { display: block; - margin: 10px 0 0 10px; + margin: 10px 0 0 4px; } .warning { - @include themify($themes) { - color: themed("red-1"); - } + color: var(--danger); } // Advisor @@ -738,17 +1070,14 @@ svg { } .no-insight { - @include themify($themes) { - color: themed("grey-1"); - } - + color: var(--text-dim); margin: 20px; } .insight { font-size: 14px; - border-left: 5px solid; - padding-left: 5px; + border-left: 4px solid; + padding-left: 10px; margin: 20px 5px 20px 0; h3 { @@ -764,78 +1093,786 @@ svg { font-size: 12px; a:not(:first-child) { - @include themify($themes) { - border-left: 1px solid themed("grey-1"); - } + border-left: 1px solid var(--border-strong); margin-left: 10px; padding-left: 10px; } } } - [level="danger"] { - border-left-color: firebrick; + [level="danger"] { + border-left-color: var(--danger); + } + + [level="warning"] { + border-left-color: var(--warn); + } + + [level="info"] { + border-left-color: var(--accent); + } + } + + // Security + @mixin security-button { + @extend .button-small; + font-size: 13px; + margin: 16px 8px; + padding: 11px; + display: inline-block; + width: auto; + } + + #security-save { + @include security-button; + } + + #security-remove { + @include security-button; + } + + .badInput input { + border-color: var(--danger); + } + + &.fadein { + top: 10px; + animation: fadein 0.2s 1 ease-out; + } + + &.fadeout { + top: 110px; + animation: fadeout 0.2s 1 ease-in; + } + + &.show { + top: 10px; + } +} + +// ── Add-account pages (rendered inside #info) ─────────────────────────── +.page-title { + font-size: 18px; + font-weight: 800; + letter-spacing: -0.01em; + margin: 2px 0 16px; + color: var(--text); +} + +// Settings page (PreferencesPage) — matches design "08 OPTIONS" +.settings-page { + .settings-section-title { + font-size: 11px; + font-weight: 700; + letter-spacing: 0.08em; + text-transform: uppercase; + color: var(--text-faint); + margin: 18px 2px 8px; + } + + .settings-row { + display: flex; + align-items: center; + gap: 12px; + padding: 13px 14px; + margin-bottom: 8px; + border-radius: 13px; + background: var(--row); + + &.clickable { + cursor: pointer; + + &:hover { + background: var(--row-hover); + } + } + + &.disabled { + opacity: 0.5; + } + } + + .settings-row-text { + flex: 1; + min-width: 0; + } + + .settings-row-title { + font-size: 14px; + font-weight: 700; + color: var(--text); + } + + .settings-row-sub { + font-size: 12px; + color: var(--text-faint); + margin-top: 2px; + } + + .settings-select, + .settings-num { + flex: none; + padding: 8px 10px; + border-radius: 10px; + border: 1px solid var(--border-strong); + background: var(--app-bg); + color: var(--text); + font-family: var(--font-sans); + font-size: 13px; + outline: none; + + &:focus { + border-color: var(--accent); + } + } + + .settings-select { + cursor: pointer; + } + + .settings-num { + width: 64px; + text-align: center; + } + + .settings-action { + width: 100%; + margin-top: 16px; + padding: 13px; + border: 1px solid var(--border-strong); + border-radius: var(--radius-row); + background: var(--app-bg); + color: var(--text); + font-family: var(--font-sans); + font-size: 14px; + font-weight: 600; + cursor: pointer; + + &:hover { + background: var(--row); + } + } +} + +// Backup page (design "16 BACKUP") +.backup-page { + .backup-section-title { + font-size: 11px; + font-weight: 700; + letter-spacing: 0.08em; + text-transform: uppercase; + color: var(--text-faint); + margin: 18px 2px 8px; + } + + .backup-warning { + padding: 13px; + border-radius: 13px; + background: var(--warn-soft); + color: var(--text-dim); + font-size: 12px; + line-height: 1.5; + margin-bottom: 8px; + } + + .backup-list { + display: flex; + flex-direction: column; + gap: 8px; + } + + .backup-row { + display: flex; + align-items: center; + gap: 12px; + padding: 13px 14px; + border-radius: 13px; + background: var(--row); + color: var(--text); + text-decoration: none; + cursor: pointer; + + &:hover { + background: var(--row-hover); + } + } + + .backup-row.disabled { + cursor: default; + opacity: 0.55; + + &:hover { + background: var(--row); + } + + .backup-chevron { + display: none; + } + } + + .backup-ico { + width: 18px; + height: 18px; + flex: none; + color: var(--text-dim); + fill: none; + } + + .cloud-chip { + width: 30px; + height: 30px; + border-radius: 9px; + flex: none; + display: flex; + align-items: center; + justify-content: center; + + svg { + width: 17px; + height: 17px; + } + } + + .backup-row-text { + flex: 1; + min-width: 0; + } + + .backup-row-title { + font-size: 14px; + font-weight: 700; + color: var(--text); + } + + .backup-row-sub { + font-size: 12px; + color: var(--text-faint); + + &.connected { + color: var(--ok); + } + } + + .backup-chevron { + width: 18px; + height: 18px; + flex: none; + color: var(--text-faint); + fill: none; + } +} + +// Pill toggle +.pill { + flex: none; + width: 46px; + height: 27px; + border-radius: var(--radius-pill); + background: var(--border-strong); + position: relative; + transition: background 0.2s; + + .pill-knob { + position: absolute; + top: 2px; + left: 2px; + width: 23px; + height: 23px; + border-radius: var(--radius-pill); + background: #fff; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); + transition: transform 0.2s; + } + + &.on { + background: var(--accent); + + .pill-knob { + transform: translateX(19px); + } + } +} + +.method-card { + display: flex; + align-items: center; + gap: 14px; + padding: 15px; + margin-bottom: 11px; + border-radius: 16px; + background: var(--row); + border: 1px solid var(--border); + cursor: pointer; + + &:hover { + background: var(--row-hover); + } + + .method-ico { + width: 44px; + height: 44px; + border-radius: 13px; + flex: none; + display: flex; + align-items: center; + justify-content: center; + background: var(--app-bg); + border: 1px solid var(--border); + color: var(--text-dim); + + svg { + width: 22px; + height: 22px; + } + } + + .method-text { + flex: 1; + font-size: 15px; + font-weight: 700; + color: var(--text); + } + + .method-chevron { + width: 18px; + height: 18px; + color: var(--text-faint); + flex: none; + } +} + +.method-card--primary { + background: var(--accent-soft); + border-color: var(--accent); + + .method-ico { + background: var(--accent); + border-color: var(--accent); + color: var(--accent-fg); + } + + .method-chevron { + color: var(--accent); + } +} + +// Manual entry form (design "07 MANUAL ENTRY") +// Scoped under #info so its label/input rules beat the generic `#info label`. +#info .manual-form { + height: 100%; + display: flex; + flex-direction: column; + gap: 16px; + + .field-label { + display: block; + font-size: 12px; + font-weight: 700; + color: var(--text-dim); + letter-spacing: 0.02em; + margin-bottom: 7px; + } + + .field-input { + width: 100%; + padding: 13px; + border-radius: 12px; + border: 1px solid var(--border-strong); + background: var(--row); + color: var(--text); + font-family: var(--font-sans); + font-size: 14px; + outline: none; + + &:focus { + border-color: var(--accent); + background: var(--app-bg); + } + + &.mono { + font-family: var(--font-mono); + letter-spacing: 1px; + } + + &:disabled { + opacity: 0.5; + cursor: not-allowed; + } + } + + select.field-input { + cursor: pointer; + } + + .advanced summary { + font-size: 13px; + font-weight: 700; + color: var(--text-dim); + cursor: pointer; + padding: 4px 0; + } + + .advanced[open] summary { + margin-bottom: 12px; + } + + .advanced .field + .field { + margin-top: 12px; + } + + .add-submit { + margin-top: auto; + width: 100%; + padding: 15px; + border: none; + border-radius: 14px; + background: var(--accent); + color: var(--accent-fg); + font-family: var(--font-sans); + font-size: 15px; + font-weight: 700; + cursor: pointer; + + &:hover { + filter: brightness(1.06); + } + } +} + +// ── Cloud provider connected page (design "17b DROPBOX CONNECTED") ─────── +#info .cloud-page { + height: 100%; + display: flex; + flex-direction: column; + gap: 16px; + + .cloud-head { + display: flex; + align-items: center; + gap: 12px; + } + + .cloud-title { + flex: 1; + font-size: 18px; + font-weight: 800; + letter-spacing: -0.01em; + } + + .cloud-brand { + width: 30px; + height: 30px; + border-radius: 9px; + flex: none; + background: oklch(0.6 0.16 245); + display: flex; + align-items: center; + justify-content: center; + + svg { + width: 17px; + height: 17px; + } + } + + .cloud-account { + display: flex; + align-items: center; + gap: 13px; + padding: 15px; + border-radius: 15px; + background: var(--row); + border: 1px solid var(--border); + } + + .cloud-avatar { + width: 44px; + height: 44px; + border-radius: var(--radius-pill); + flex: none; + background: oklch(0.86 0.07 245); + color: oklch(0.42 0.16 245); + display: flex; + align-items: center; + justify-content: center; + font-weight: 800; + font-size: 18px; + } + + .cloud-account-text { + flex: 1; + min-width: 0; + } + + .cloud-email { + font-size: 14px; + font-weight: 700; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + .cloud-status { + display: flex; + align-items: center; + gap: 5px; + font-size: 12px; + font-weight: 600; + color: var(--ok); + + .cloud-dot { + width: 7px; + height: 7px; + border-radius: var(--radius-pill); + background: var(--ok); + } + } + + .field-label { + display: block; + font-size: 12px; + font-weight: 700; + color: var(--text-dim); + margin-bottom: 7px; + } + + .cloud-select-wrap { + position: relative; + } + + .cloud-select { + width: 100%; + padding: 13px 36px 13px 13px; + border-radius: 12px; + border: 1px solid var(--border-strong); + background: var(--row); + color: var(--text); + font-family: var(--font-sans); + font-size: 14px; + font-weight: 600; + outline: none; + appearance: none; + cursor: pointer; + + &:focus { + border-color: var(--accent); + } + } + + .cloud-select-chevron { + position: absolute; + right: 13px; + top: 50%; + transform: translateY(-50%); + width: 14px; + height: 14px; + color: var(--text-faint); + pointer-events: none; + } + + .cloud-warning { + display: flex; + gap: 10px; + padding: 13px; + border-radius: 13px; + background: var(--warn-soft); + color: var(--text-dim); + + svg { + flex: none; + width: 17px; + height: 17px; + stroke: var(--warn); + margin-top: 1px; + } + + div { + font-size: 12px; + line-height: 1.5; + } + } + + .cloud-actions { + margin-top: auto; + display: flex; + flex-direction: column; + gap: 9px; + } + + .cloud-primary { + width: 100%; + padding: 14px; + border: none; + border-radius: 13px; + background: var(--accent); + color: var(--accent-fg); + font-family: var(--font-sans); + font-size: 14.5px; + font-weight: 700; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + + svg { + width: 17px; + height: 17px; + } + + &:hover { + filter: brightness(1.06); } + } - [level="warning"] { - border-left-color: darkorange; + .cloud-logout { + width: 100%; + padding: 12px; + border: 1px solid var(--border-strong); + border-radius: 13px; + background: transparent; + color: var(--text-dim); + font-family: var(--font-sans); + font-size: 13.5px; + font-weight: 700; + cursor: pointer; + + &:hover { + border-color: var(--danger); + color: var(--danger); } + } - [level="info"] { - border-left-color: #08c; + .cloud-connect { + flex: 1; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 20px; + } + + .cloud-brand-lg { + width: 64px; + height: 64px; + border-radius: 18px; + flex: none; + background: oklch(0.6 0.16 245); + display: flex; + align-items: center; + justify-content: center; + + svg { + width: 32px; + height: 32px; } } - // Security - @mixin security-button($margin-left) { - @extend .button-small; - font-size: 12px; - margin: 20px 100px; - padding: 10px; - display: inline-block; - width: 80px; - margin-left: $margin-left; - margin-right: 0; + .cloud-connecting { + display: flex; + align-items: center; + gap: 10px; + font-size: 14px; + font-weight: 600; + color: var(--text-dim); } - #security-save { - @include security-button(40px); + .dropbox-spinner { + width: 20px; + height: 20px; + border-radius: var(--radius-pill); + border: 3px solid var(--ring-track); + border-top-color: var(--accent); + animation: spin 0.8s linear infinite; } - #security-remove { - @include security-button(30px); + // Google Drive branding (multicolor logo on a neutral chip; red-ish avatar) + &.drive { + .cloud-brand { + background: var(--row); + border: 1px solid var(--border); + } + + .cloud-avatar { + background: oklch(0.86 0.07 25); + color: oklch(0.45 0.18 25); + } } - .badInput { - @include themify($themes) { - input { - border-color: themed("red-1"); - } + // Connect prompt (design "17c CONNECT DRIVE") + .cloud-hero { + flex: 1; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; + } + + .cloud-hero-logo { + width: 78px; + height: 78px; + border-radius: 22px; + background: var(--row); + border: 1px solid var(--border); + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 22px; + + svg { + width: 40px; + height: 40px; } } - &.fadein { - top: 10px; - animation: fadein 0.2s 1 ease-out; + .cloud-hero-title { + font-size: 22px; + font-weight: 800; + letter-spacing: -0.02em; + margin-bottom: 10px; } - &.fadeout { - top: 110px; - animation: fadeout 0.2s 1 ease-in; + .cloud-hero-desc { + font-size: 13.5px; + line-height: 1.55; + color: var(--text-dim); + max-width: 270px; } - &.show { - top: 10px; + .cloud-foot { + display: flex; + flex-direction: column; + gap: 8px; + } + + .cloud-note { + display: flex; + align-items: center; + gap: 9px; + padding: 11px 13px; + border-radius: 12px; + background: var(--row); + color: var(--text-dim); + font-size: 12.5px; + + svg { + flex: none; + width: 15px; + height: 15px; + stroke: var(--ok); + } } } -// Menu +// ── Slide-in menu ─────────────────────────────────────────────────────── #menu { - width: 320px; - height: 480px; + width: $popup-w; + height: $popup-h; position: absolute; left: -1000px; top: 0; + background: var(--app-bg); &.slidein { left: 0; @@ -851,32 +1888,32 @@ svg { #menuBody { overflow-y: auto; - height: 442px; + height: $popup-h - $header-h; width: inherit; position: absolute; - @include themify($themes) { - background: themed("grey-background"); - } + background: var(--app-bg); + padding: 8px; } .menuList { - margin: 10px; - border-radius: 2px; - @include themify($themes) { - border: themed("grey-2") 1px solid; - background: themed("white-1"); - } + margin: 8px; + border-radius: var(--radius-row); + overflow: hidden; + background: var(--row); p { position: relative; - padding: 10px; - font-size: 16px; + padding: 13px 14px; + font-size: 14.5px; + font-weight: 600; cursor: pointer; display: grid; grid-template-columns: 30px auto; - @include themify($themes) { - border-bottom: themed("grey-2") 1px solid; - color: themed("grey-1"); + align-items: center; + color: var(--text-dim); + + &:not(:last-child) { + border-bottom: 1px solid var(--border); } span { @@ -884,76 +1921,60 @@ svg { align-items: center; svg { - @include themify($themes) { - fill: themed("grey-1"); - } - height: 16px; - width: 16px; + fill: var(--text-dim); + height: 17px; + width: 17px; } } &:hover { - @include themify($themes) { - background: themed("blue-menu"); - color: themed("black-1"); - } + background: var(--row-hover); + color: var(--text); svg { - @include themify($themes) { - fill: themed("black-1"); - } + fill: var(--text); } } - - &:last-child { - border-bottom: none; - } } } } #version { text-align: center; - margin: 10px; - @include themify($themes) { - color: themed("grey-1"); - } -} - -// Animations -@keyframes timer { - to { - stroke-dashoffset: -25.12; - } + margin: 16px 10px; + font-size: 12px; + color: var(--text-faint); + font-family: var(--font-mono); } -@keyframes twinkling { - 0% { - color: #eea59c; - } +// ── Animations ────────────────────────────────────────────────────────── +@keyframes glint { + 0%, 100% { - color: #dd4b39; + opacity: 1; + } + 50% { + opacity: 0.35; } } -@keyframes flash { - 0%, - 33% { +@keyframes toastUp { + from { opacity: 0; + transform: translate(-50%, 12px); } - 33.1%, - 100% { + to { opacity: 1; + transform: translate(-50%, 0); } } -@keyframes glint { - 0%, - 100% { - opacity: 1; +@keyframes sheetUp { + from { + transform: translateY(100%); } - 50% { - opacity: 0.15; + to { + transform: translateY(0); } } @@ -1019,351 +2040,415 @@ svg { } } -// Misc -@font-face { - font-family: "Droid Sans Mono"; - font-style: normal; - font-weight: 400; - src: url(DroidSansMono.woff2) format("woff2"); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, - U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000; -} - .gu-mirror { display: none; } +// ── Scrollbars ────────────────────────────────────────────────────────── ::-webkit-scrollbar { - width: 10px; - @include themify($themes) { - background: themed("grey-3"); - } + width: 8px; + height: 8px; } ::-webkit-scrollbar-thumb { - @include themify($themes) { - background-color: themed("grey-1"); - border: 2px solid themed("grey-3"); - } - border-radius: 5px; + background: var(--border-strong); + border-radius: 8px; } -// Dark overrides -.theme-dark { - #codes { - &:not(.edit) { - .code.timeout:not(.hotp) { - animation: glint 1s infinite; - } - } - } - - .message-box, - #info { - border: #ccc 1px solid; - box-shadow: 1px 1px 3px black; - } - - .entry .issuerEdit input { - color: #ccc; - } +::-webkit-scrollbar-track { + background: transparent; +} - ::-webkit-scrollbar { - background: #1e1e1e !important; - } +// ── Onboarding (first run) ────────────────────────────────────────────── +.onboarding { + height: $popup-h; + display: flex; + flex-direction: column; + padding: 34px 30px 30px; + background: var(--app-bg); + color: var(--text); - ::-webkit-scrollbar-thumb { - background-color: rgba(255, 255, 255, 0.5) !important; - border: 2px solid #1e1e1e !important; + .onboarding-hero { + flex: 1; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; } -} -// Simple & Compact overrides -.theme-simple, -.theme-compact { - .header { - border-bottom: white 1px solid !important; + .onboarding-logo { + width: 78px; + height: 78px; + border-radius: 22px; + background: var(--accent); + color: var(--accent-fg); + display: flex; + align-items: center; + justify-content: center; + box-shadow: 0 10px 26px var(--accent-soft); + margin-bottom: 26px; - &::after { - content: ""; - display: block; - margin: 0 20px -1px 20px; - border-bottom: #eee 1px solid; + svg { + width: 40px; + height: 40px; } } - #search { - border-color: white !important; + .onboarding-title { + font-size: 24px; + font-weight: 800; + letter-spacing: -0.02em; + margin-bottom: 10px; } - .entry { - border-color: white !important; + .onboarding-sub { + font-size: 14px; + line-height: 1.5; + color: var(--text-dim); + max-width: 270px; } - &:not(.edit) { - .entry:hover { - background: #f8f8f8; + .onboarding-features { + display: flex; + flex-direction: column; + gap: 11px; + margin-bottom: 22px; + + .feature { + display: flex; + align-items: center; + gap: 11px; + font-size: 13.5px; + color: var(--text-dim); + + .dot { + width: 7px; + height: 7px; + border-radius: var(--radius-pill); + flex: none; + } } } - .control-group select { - border-color: white !important; - } - - .menuList { - border-color: white !important; + .onboarding-primary { + width: 100%; + padding: 15px; + border: none; + border-radius: 14px; + background: var(--accent); + color: var(--accent-fg); + font-family: var(--font-sans); + font-size: 15px; + font-weight: 700; + cursor: pointer; - p { - border-color: white !important; + &:hover { + filter: brightness(1.06); } } - ::-webkit-scrollbar { - background-color: white !important; - } + .onboarding-secondary { + width: 100%; + padding: 13px; + margin-top: 8px; + border: none; + border-radius: 14px; + background: transparent; + color: var(--text-dim); + font-family: var(--font-sans); + font-size: 13.5px; + font-weight: 600; + cursor: pointer; - ::-webkit-scrollbar-thumb { - border: 2px solid white !important; + &:hover { + color: var(--text); + } } +} - .button { - background: #f2f2f2 !important; - border: #f2f2f2 1px solid !important; - color: black !important; +// ── Loading (design "13b LOADING") ────────────────────────────────────── +.loading-page { + height: 100%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 22px; - &:hover { - background: #f8f8f8 !important; - border: #f8f8f8 1px solid !important; - } + .loading-spinner { + width: 52px; + height: 52px; + border-radius: var(--radius-pill); + border: 4px solid var(--ring-track); + border-top-color: var(--accent); + animation: spin 0.8s linear infinite; } - #info { - box-shadow: none !important; + .loading-text { + font-size: 15px; + font-weight: 700; + color: var(--text-dim); + letter-spacing: 0.02em; } +} - .message-box { - border: #ccc 1px solid; - box-shadow: none !important; +@keyframes spin { + to { + transform: rotate(360deg); } } -// Compact overrides -.theme-compact { - #codes { - &:not(.edit) { - .entry { - padding: 0 10px; - margin: 0 0 0 10px; - } - } +// Inline "connecting…" indicator on the cloud backup sign-in pages. +.dropbox-connecting { + display: flex; + align-items: center; + gap: 8px; + margin: 10px 0 0 20px; + color: var(--text-dim); + + .dropbox-spinner { + width: 14px; + height: 14px; + border-radius: var(--radius-pill); + border: 2px solid var(--ring-track); + border-top-color: var(--accent); + animation: spin 0.8s linear infinite; } +} - .entry { - .sector { - bottom: 0; - } +// ── Manage password (design "14 MANAGE PASSWORD") ─────────────────────── +// Scoped under #info so its label rules beat the generic `#info label`. +#info .pw-page { + height: 100%; + display: flex; + flex-direction: column; + gap: 14px; + + .pw-warning { + display: flex; + gap: 10px; + padding: 13px; + border-radius: 13px; + background: var(--warn-soft); - .showqr, - .pin { - top: 0; + svg { + flex: none; + width: 18px; + height: 18px; + stroke: var(--warn); + margin-top: 1px; } - .code { - font-size: 32px; - margin-top: -5px; - letter-spacing: 0.5rem; + div { + font-size: 12px; + line-height: 1.5; + color: var(--text-dim); } } - a.entry { - border-bottom: #eee 1px solid !important; + .pw-field label { + display: block; + font-size: 12px; + font-weight: 700; + color: var(--text-dim); + margin-bottom: 7px; } - .issuer.account { - display: none; - } -} + .pw-input { + width: 100%; + padding: 13px; + border-radius: 12px; + border: 1px solid var(--border-strong); + background: var(--row); + color: var(--text); + font-family: var(--font-mono); + font-size: 14px; + letter-spacing: 2px; + outline: none; -// Accessibility overrides -.theme-accessibility { - select { - border-color: white; - color: white; - background-color: black; - } - .entry { - .issuerEdit { - input { - color: white; - background: black; - border: white 1px solid; - } + &:focus { + border-color: var(--accent); + background: var(--app-bg); } + } - .showqr, - .pin { - svg { - fill: yellow; - } + .pw-strength { + display: flex; + gap: 8px; + margin-top: 9px; - &:hover { - svg { - fill: yellow; - } - } + .seg { + flex: 1; + height: 5px; + border-radius: var(--radius-pill); + transition: background 0.3s; } } - #menu { - .menuList { - p:hover { - color: black; + .pw-save { + margin-top: auto; + width: 100%; + padding: 15px; + border: none; + border-radius: 14px; + background: var(--accent); + color: var(--accent-fg); + font-family: var(--font-sans); + font-size: 15px; + font-weight: 700; + cursor: pointer; - svg { - fill: black; - } - } + &:hover { + filter: brightness(1.06); } } - .header { - .icon { - svg { - fill: yellow; - } - - &#i-sync { - svg { - fill: white; - } - - &:hover { - svg { - fill: white; - } - } - } + .pw-remove { + width: 100%; + padding: 13px; + border: 1px solid var(--danger); + border-radius: 14px; + background: transparent; + color: var(--danger); + font-family: var(--font-sans); + font-size: 14px; + font-weight: 700; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + gap: 8px; - &:hover { - svg { - fill: yellow; - } - } + svg { + width: 16px; + height: 16px; } - } - #codes { - &:not(.edit) { - .code.timeout:not(.hotp) { - animation: flash 1s infinite; - } + &:hover { + background: var(--row); } } +} - #info { - border: 1px solid white; - box-shadow: none; - } +// ── Unlock screen (design "13 LOCKED") ───────────────────────────────── +.lock-page { + height: 100%; + display: flex; + flex-direction: column; - #info .control-group input[type="checkbox"]::before { - background: none; - border: 2px solid white; - box-sizing: border-box; + .lock-hero { + flex: 1; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; } - #info .control-group input[type="checkbox"]::after { - border: 2px solid black; - margin: -2px; - box-shadow: none; + .lock-icon { + width: 80px; + height: 80px; + border-radius: 24px; + background: var(--accent-soft); + color: var(--accent); + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 24px; + + svg { + width: 38px; + height: 38px; + } } - #info .control-group input[type="checkbox"]:checked::before { - background: white; + .lock-title { + font-size: 22px; + font-weight: 800; + letter-spacing: -0.02em; + margin-bottom: 8px; } - .input { - color: black; + .lock-desc { + font-size: 13.5px; + line-height: 1.5; + color: var(--text-dim); + max-width: 250px; + margin-bottom: 24px; } - #notification { - &.fadein { - opacity: 1; - animation: none; - } + .lock-input { + width: 100%; + padding: 14px; + border-radius: 13px; + border: 1px solid var(--border-strong); + background: var(--row); + color: var(--text); + font-family: var(--font-mono); + font-size: 16px; + letter-spacing: 3px; + text-align: center; + outline: none; - &.fadeout { - opacity: 0; - animation: none; + &:focus { + border-color: var(--accent); + background: var(--app-bg); } - } - ::-webkit-scrollbar { - background: black !important; - } - - ::-webkit-scrollbar-thumb { - background-color: white !important; - border: 2px solid black !important; + &.badInput { + border-color: var(--danger); + } } -} -// Flat overrides -.theme-flat { - .header { - color: black; - background: white; - border-bottom: #f5f4f7 1px solid; + .lock-error { + display: block; + color: var(--danger); + font-size: 12.5px; + margin-top: 10px; } - #codes { - background: #fcfbff; + .lock-unlock { + width: 100%; + padding: 15px; + border: none; + border-radius: 14px; + background: var(--accent); + color: var(--accent-fg); + font-family: var(--font-sans); + font-size: 15px; + font-weight: 700; + cursor: pointer; - .entry { - border: #f5f4f7 1px solid; - border-radius: 8px; + &:hover { + filter: brightness(1.06); } } +} - #menu { - #menuBody { - background: #fcfbff; - - .menuList { - margin: 10px; - border-radius: 8px; - border: #f5f4f7 1px solid; - background: white; - - p { - color: #727272; - - &:not(:last-child) { - border-bottom: #f5f4f7 1px solid; - } +// ── Compact density ───────────────────────────────────────────────────── +.theme-compact { + .entry { + padding: 7px 12px; + gap: 10px; - span svg { - fill: #727272; - } - } - } + .monogram { + width: 30px; + height: 30px; + font-size: 14px; + border-radius: 9px; } - #version { - bottom: 0px; - position: relative; - margin: 10px auto; - width: 100%; - color: #727272; - font-size: 0.9em; + .code { + font-size: 18px; } - #info { - border-radius: 8px; - border: #f5f4f7 1px solid; - - .control-group { - border-bottom: #f5f4f7 1px solid; - } + .account { + display: none; } } + + #codes { + gap: 4px; + } } diff --git a/scripts/build.sh b/scripts/build.sh index 3da00c0e2..30afc4128 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -19,7 +19,7 @@ if [[ $PLATFORM != "chrome" ]] && [[ $PLATFORM != "firefox" ]] && [[ $PLATFORM ! fi echo "Removing old build files..." -rm -rf build dist +rm -rf build js rm -rf firefox chrome edge release test echo "Checking style..." if ./node_modules/.bin/prettier --check $STYLEFILES 1> /dev/null ; then @@ -46,8 +46,6 @@ if ! [[ $REMOTE = *"https://github.com/Authenticator-Extension/Authenticator.git echo -e "Thanks for forking Authenticator! If you plan on redistributing your own version of Authenticator please generate your own API keys and put them in ./src/models/credentials.ts and ./manifest-chrome.json" echo "Clear this warning by commenting it out in ./scripts/build.sh" echo - read -rsp $'Press any key to continue...\n' -n1 key - echo fi echo "Compiling..." @@ -72,7 +70,7 @@ fi postCompile () { mkdir $1 - cp -r dist css images _locales LICENSE view $1 + cp -r js css images _locales LICENSE view $1 if [[ $PLATFORM == "test" ]]; then cp manifests/manifest-$1-testing.json $1/manifest.json diff --git a/scripts/check-i18n.js b/scripts/check-i18n.js new file mode 100644 index 000000000..a647a56b8 --- /dev/null +++ b/scripts/check-i18n.js @@ -0,0 +1,54 @@ +// Verifies every _locales//messages.json has EXACTLY the same key set as +// the en source (which src/store/i18n.ts iterates to build the runtime strings). +// Exits non-zero on any missing/extra key or invalid JSON so CI fails. +// Replaces the old Crowdin-era autofill (scripts/i18n.js); this fork maintains +// translations by hand and has no Crowdin sync. +"use strict"; + +const fs = require("fs"); +const path = require("path"); + +const dir = "_locales"; +const read = (f) => + JSON.parse(fs.readFileSync(f, "utf8").replace(/^/, "")); + +const enKeys = Object.keys(read(path.join(dir, "en", "messages.json"))); +const enSet = new Set(enKeys); + +let failed = false; +for (const loc of fs.readdirSync(dir)) { + const file = path.join(dir, loc, "messages.json"); + if (loc === "en" || !fs.existsSync(file)) continue; + + let keys; + try { + keys = Object.keys(read(file)); + } catch (e) { + console.error(`x ${loc}: invalid JSON - ${e.message}`); + failed = true; + continue; + } + + const kset = new Set(keys); + const missing = enKeys.filter((k) => !kset.has(k)); + const extra = keys.filter((k) => !enSet.has(k)); + if (missing.length || extra.length) { + failed = true; + console.error(`x ${loc}: ${missing.length} missing, ${extra.length} extra`); + if (missing.length) console.error(` missing: ${missing.join(", ")}`); + if (extra.length) console.error(` extra: ${extra.join(", ")}`); + } +} + +if (failed) { + console.error( + "\nLocale key sets diverge from _locales/en/messages.json (the source of truth)." + ); + console.error( + "Every locale must have exactly the same keys as en. Add new strings to en" + ); + console.error("and to every locale, or remove them everywhere."); + process.exit(1); +} + +console.log(`all locales match en (${enKeys.length} keys)`); diff --git a/scripts/deploy-key.gpg b/scripts/deploy-key.gpg deleted file mode 100644 index fb4381333..000000000 Binary files a/scripts/deploy-key.gpg and /dev/null differ diff --git a/scripts/i18n.js b/scripts/i18n.js deleted file mode 100644 index 745ec5827..000000000 --- a/scripts/i18n.js +++ /dev/null @@ -1,33 +0,0 @@ -// This checks for new stings from _locales/en/messages.json and adds them -// to other translation files. Used by i18n.sh -'use strict'; - -const fs = require('fs'); - -function readFile(filename) { - let data = fs.readFileSync(filename); - data = data.toString().replace(/^\uFEFF/, ''); - return JSON.parse(data); -} - -let strings = readFile('_locales/en/messages.json'); -let currentFile; -let missingKeys = []; - -fs.readdir('_locales', function(err, items) { - for (let i=0; i !currentFile.hasOwnProperty(string)); - if (!missingKeys[0]) { - continue; - } - for (let j=0; j { - if (err) throw err; - }) - } - } - } -}); diff --git a/scripts/i18n.sh b/scripts/i18n.sh deleted file mode 100644 index fa9c753f9..000000000 --- a/scripts/i18n.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/bash -# This script automatically adds new translation strings from _locales/en/messages.json to other locale files - -# Define colors -RED='\033[0;31m' -GREEN='\033[0;32m' -NC='\033[0m' - -BRANCH=${GITHUB_REF##*/} - -# Configure git -git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" -git config --global user.name "github-actions[bot]" -git remote set-url origin git@github.com:${GITHUB_REPOSITORY}.git -gpg --quiet --batch --yes --decrypt --passphrase="$DEPLOY_KEY_PASSWORD" \ - --output $GITHUB_WORKSPACE/scripts/deploy-key $GITHUB_WORKSPACE/scripts/deploy-key.gpg -chmod 600 $GITHUB_WORKSPACE/scripts/deploy-key -eval `ssh-agent -s` &> /dev/null -ssh-add $GITHUB_WORKSPACE/scripts/deploy-key &> /dev/null - -# Fix i18n issues -cd $GITHUB_WORKSPACE -node ./scripts/i18n.js - -# Branch changes and error with details on how to fix i18n if branched -if [[ `git diff _locales` ]]; then - git checkout $BRANCH &> /dev/null - git add ./_locales/*/messages.json - git commit -m "Add new strings" -m "This commit was automatically made by run $GITHUB_RUN_ID" --quiet - git push --quiet - printf "${RED}You added new strings to _locales/en/messages.json, but not some of the other translation files. A commit has been created on the current branch with the required changes already made. ${NC}\n" - exit 0 -else - printf "${GREEN}No new translation strings detected.${NC}" -fi diff --git a/src/argon.ts b/src/argon.ts index ea27b3967..53a086c56 100644 --- a/src/argon.ts +++ b/src/argon.ts @@ -11,13 +11,14 @@ window.addEventListener("message", (event) => { switch (message.action) { case "hash": Argon.hash(message.value, message.salt).then((hash) => { - source.postMessage({ response: hash }, event.origin); + // echo the request id so the caller can match the reply to its request + source.postMessage({ id: message.id, response: hash }, event.origin); }); break; case "verify": Argon.compareHash(message.hash, message.value).then((result) => { - source.postMessage({ response: result }, event.origin); + source.postMessage({ id: message.id, response: result }, event.origin); }); break; @@ -50,8 +51,10 @@ class Argon { encoded: hash, }) .then(() => resolve(true)) - .catch((e: { message: string; code: number }) => { - console.error("Error decoding hash", e); + .catch(() => { + // verify() rejects on a wrong passphrase (the common case) as well as + // on a malformed hash; both just mean "cannot unlock", so report a + // non-match instead of logging an error for every mistyped password. resolve(false); }); }); diff --git a/src/background.ts b/src/background.ts index bb59a2f9d..e5affb3b2 100644 --- a/src/background.ts +++ b/src/background.ts @@ -1,7 +1,7 @@ import { getCredentials } from "./models/credentials"; import { Encryption } from "./models/encryption"; import { EntryStorage, ManagedStorage } from "./models/storage"; -import { Dropbox, Drive, OneDrive } from "./models/backup"; +import { Dropbox, OneDrive } from "./models/backup"; import { getSiteName, getMatchedEntries, @@ -14,60 +14,98 @@ import { getOTPAuthPerLineFromOPTAuthMigration } from "./models/migration"; import { isChrome, isFirefox } from "./browser"; import { UserSettings } from "./models/settings"; -let contentTab: chrome.tabs.Tab | undefined; - -chrome.runtime.onMessage.addListener(async (message, sender) => { - await UserSettings.updateItems(); +chrome.runtime.onMessage.addListener((message, sender) => { + // Only act on messages from our own extension pages / content scripts, never + // another extension. (No externally_connectable is set, so web pages can't + // reach here, but this is cheap defense-in-depth for the sensitive actions + // below — cache passphrase, cloud backup, lock.) + if (sender.id !== chrome.runtime.id) { + return; + } - if (message.action === "getCapture") { - if (!sender.tab) { - return; - } - const url = await getCapture(sender.tab); - if (contentTab && contentTab.id) { + // None of these handlers send a response, so do the async work fire-and-forget + // and DON'T return true. Returning true kept the message channel open waiting + // for a sendResponse that never came, so the sender's sendMessage promise + // rejected with "message channel closed before a response was received" + // (e.g. when a QR capture finds no code). + void (async () => { + await UserSettings.updateItems(); + + if (message.action === "getCapture") { + // Use sender.tab, not a module-level tab ref: in MV3 the service worker + // can be torn down between framing the QR region and this message + // arriving, leaving such a ref undefined -- so the capture reply was + // never sent back and the scan silently did nothing. sender.tab is the + // content script that asked, so it is always the right (and live) tab. + if (!sender.tab || sender.tab.id === undefined) { + return; + } + const url = await getCapture(sender.tab); message.info.url = url; - chrome.tabs.sendMessage(contentTab.id, { + chrome.tabs.sendMessage(sender.tab.id, { action: "sendCaptureUrl", info: message.info, }); + } else if (message.action === "getTotp") { + getTotp(message.info, sender.tab?.id, false, hostnameFromTab(sender.tab)); + } else if (message.action === "cachePassphrase") { + chrome.storage.session.set({ + cachedPassphrase: message.value, + cachedKeyId: message.keyId, + }); + chrome.alarms.clear("autolock"); + setAutolock(); + } else if (["dropbox", "onedrive"].indexOf(message.action) > -1) { + getBackupToken(message.action); + } else if (message.action === "lock") { + chrome.storage.session.set({ cachedPassphrase: null, cachedKeyId: null }); + } else if (message.action === "resetAutolock") { + chrome.alarms.clear("autolock"); + setAutolock(); + } else if (message.action === "updateContentTab") { + // Persist in session storage so the autolock alarm can still reach this + // tab to dismiss the capture overlay after the MV3 worker recycles. + chrome.storage.session.set({ captureTabId: message.data?.id }); + } else if (message.action === "updateContextMenu") { + updateContextMenu(); } - } else if (message.action === "getTotp") { - getTotp(message.info); - } else if (message.action === "cachePassphrase") { - chrome.storage.session.set({ - cachedPassphrase: message.value, - cachedKeyId: message.keyId, - }); - chrome.alarms.clear("autolock"); - setAutolock(); - } else if (["dropbox", "drive", "onedrive"].indexOf(message.action) > -1) { - getBackupToken(message.action); - } else if (message.action === "lock") { - chrome.storage.session.set({ cachedPassphrase: null, cachedKeyId: null }); - } else if (message.action === "resetAutolock") { - chrome.alarms.clear("autolock"); - setAutolock(); - } else if (message.action === "updateContentTab") { - contentTab = message.data; - } else if (message.action === "updateContextMenu") { - updateContextMenu(); - } - - // https://stackoverflow.com/a/56483156 - return true; + })(); }); chrome.alarms.onAlarm.addListener(() => { - chrome.storage.session.set({ cachedPassphrase: null, cachedKeyId: null }); - if (contentTab && contentTab.id) { - chrome.tabs.sendMessage(contentTab.id, { action: "stopCapture" }); - } - chrome.runtime.sendMessage({ action: "stopImport" }); - - // https://stackoverflow.com/a/56483156 - return true; + void (async () => { + await chrome.storage.session.set({ + cachedPassphrase: null, + cachedKeyId: null, + }); + // captureTabId is persisted in session storage (like the cached passphrase) + // so it survives MV3 service-worker recycling. The old module-level tab ref + // was undefined in a fresh worker, so a QR capture overlay left open stayed + // stuck on the page after autolock. Dismiss it if a tab is still recorded. + const { captureTabId } = await chrome.storage.session.get("captureTabId"); + if (typeof captureTabId === "number") { + chrome.tabs + .sendMessage(captureTabId, { action: "stopCapture" }) + .catch(() => undefined); + await chrome.storage.session.remove("captureTabId"); + } + chrome.runtime.sendMessage({ action: "stopImport" }).catch(() => undefined); + })(); }); +// The host of the tab a QR scan was initiated from, used to pre-bind a newly +// imported entry to the site the user scanned it on. +function hostnameFromTab(tab?: chrome.tabs.Tab): string | undefined { + if (!tab || !tab.url) { + return undefined; + } + try { + return new URL(tab.url).hostname.toLowerCase(); + } catch { + return undefined; + } +} + async function getCapture(tab: chrome.tabs.Tab) { const dataUrl = await chrome.tabs.captureVisibleTab(tab.windowId, { format: "png", @@ -76,11 +114,19 @@ async function getCapture(tab: chrome.tabs.Tab) { return dataUrl; } -async function getTotp(text: string, silent = false) { - if (!contentTab || !contentTab.id || !text) { +async function getTotp( + text: string, + tabId?: number, + silent = false, + host?: string +) { + // tabId is the content tab that initiated the scan (sender.tab.id). Relying on + // a module-level tab ref here broke scans whenever the MV3 service worker had + // been recycled (it was undefined -> silent return). + if (tabId === undefined || !text) { return false; } - const id = contentTab.id; + const id = tabId; if (text.indexOf("otpauth://") !== 0) { if (text.indexOf("otpauth-migration://") === 0) { @@ -92,11 +138,15 @@ async function getTotp(text: string, silent = false) { const getTotpPromises: Array> = []; for (const otpUrl of otpUrls) { - getTotpPromises.push(getTotp(otpUrl, true)); + getTotpPromises.push(getTotp(otpUrl, id, true, host)); } const getTotpResults = await Promise.allSettled(getTotpPromises); - const failedCount = getTotpResults.filter((res) => !res).length; + // allSettled entries are always-truthy {status,value} objects, so the + // old `!res` test was never true and every import reported success. + const failedCount = getTotpResults.filter( + (res) => res.status !== "fulfilled" || !res.value + ).length; if (failedCount === otpUrls.length) { !silent && chrome.tabs.sendMessage(id, { action: "migrationfail" }); return false; @@ -208,6 +258,7 @@ async function getTotp(text: string, silent = false) { account, hash, issuer, + host, secret, type, encrypted: false, @@ -240,74 +291,138 @@ async function getTotp(text: string, silent = false) { } } +// Generate a PKCE code_verifier (43+ random URL-safe chars) and its +// code_challenge (base64url-encoded SHA-256 of the verifier). +async function generatePkce(): Promise<{ + codeVerifier: string; + codeChallenge: string; +}> { + const array = new Uint8Array(48); + crypto.getRandomValues(array); + // base64url-encode the random bytes for the verifier + const codeVerifier = btoa(String.fromCharCode(...array)) + .replace(/\+/g, "-") + .replace(/\//g, "_") + .replace(/=/g, ""); + + const encoder = new TextEncoder(); + const data = encoder.encode(codeVerifier); + const digest = await crypto.subtle.digest("SHA-256", data); + const codeChallenge = btoa(String.fromCharCode(...new Uint8Array(digest))) + .replace(/\+/g, "-") + .replace(/\//g, "_") + .replace(/=/g, ""); + + return { codeVerifier, codeChallenge }; +} + function getBackupToken(service: string) { - if (isChrome && service === "drive") { - chrome.identity.getAuthToken( - { - interactive: true, - scopes: ["https://www.googleapis.com/auth/drive.file"], - }, - (value) => { - if (!value) { - return false; - } - UserSettings.items.driveToken = value; - UserSettings.commitItems(); - chrome.runtime.sendMessage({ action: "drivetoken", value }); - return true; - } - ); - } else { - let authUrl = ""; - let redirUrl = ""; - if (service === "dropbox") { - redirUrl = encodeURIComponent(chrome.identity.getRedirectURL()); - authUrl = - "https://www.dropbox.com/oauth2/authorize?response_type=token&client_id=" + - getCredentials().dropbox.client_id + + if (service === "dropbox") { + // Upgrade from implicit flow to Authorization Code + PKCE (no secret). + void (async () => { + const { codeVerifier, codeChallenge } = await generatePkce(); + const redirUrl = chrome.identity.getRedirectURL(); + const authUrl = + "https://www.dropbox.com/oauth2/authorize" + + "?response_type=code" + + "&client_id=" + + encodeURIComponent(getCredentials().dropbox.client_id) + "&redirect_uri=" + - redirUrl; - } else if (service === "drive") { - if (navigator.userAgent.indexOf("Edg") !== -1) { - redirUrl = encodeURIComponent("https://authenticator.cc/oauth-edge"); - } else if (isFirefox) { - redirUrl = encodeURIComponent(chrome.identity.getRedirectURL()); - } else { - redirUrl = encodeURIComponent("https://authenticator.cc/oauth"); - } + encodeURIComponent(redirUrl) + + "&code_challenge=" + + encodeURIComponent(codeChallenge) + + "&code_challenge_method=S256" + + "&token_access_type=offline"; + + chrome.identity.launchWebAuthFlow( + { url: authUrl, interactive: true }, + async (redirectedTo) => { + if (!redirectedTo) { + chrome.runtime + .sendMessage({ action: "dropboxauthdone" }) + .catch(() => undefined); + return; + } + // Authorization Code is in the query string: ?code=... + let code: string | undefined; + try { + const parsedUrl = new URL(redirectedTo); + code = parsedUrl.searchParams.get("code") ?? undefined; + } catch { + /* invalid URL — fall through */ + } + if (!code) { + chrome.runtime + .sendMessage({ action: "dropboxauthdone" }) + .catch(() => undefined); + return; + } + + // Exchange code for tokens (PKCE — no client_secret) + try { + const response = await fetch( + "https://api.dropboxapi.com/oauth2/token", + { + method: "POST", + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + body: + "code=" + + encodeURIComponent(code) + + "&code_verifier=" + + encodeURIComponent(codeVerifier) + + "&client_id=" + + encodeURIComponent(getCredentials().dropbox.client_id) + + "&redirect_uri=" + + encodeURIComponent(redirUrl) + + "&grant_type=authorization_code", + } + ); + const res = await response.json(); + if (res.error) { + console.error(res.error_description); + } else { + UserSettings.items.dropboxToken = res.access_token; + UserSettings.items.dropboxRefreshToken = res.refresh_token; + UserSettings.items.dropboxRevoked = false; + await UserSettings.commitItems(); + uploadBackup("dropbox"); + } + } catch (error) { + console.error(error); + } + chrome.runtime + .sendMessage({ action: "dropboxauthdone" }) + .catch(() => undefined); + } + ); + })(); + return; + } + + if (service === "onedrive") { + const redirUrl = encodeURIComponent(chrome.identity.getRedirectURL()); + const authUrl = `https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=${ + getCredentials().onedrive.client_id + }&response_type=code&redirect_uri=${redirUrl}&scope=https%3A%2F%2Fgraph.microsoft.com%2FFiles.ReadWrite${ + UserSettings.items.oneDriveBusiness !== true ? ".AppFolder" : "" + }%20https%3A%2F%2Fgraph.microsoft.com%2FUser.Read%20offline_access&response_mode=query&prompt=consent`; - authUrl = - "https://accounts.google.com/o/oauth2/v2/auth?response_type=code&access_type=offline&client_id=" + - getCredentials().drive.client_id + - "&scope=https%3A//www.googleapis.com/auth/drive.file&prompt=consent&redirect_uri=" + - redirUrl; - } else if (service === "onedrive") { - redirUrl = encodeURIComponent(chrome.identity.getRedirectURL()); - authUrl = `https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=${ - getCredentials().onedrive.client_id - }&response_type=code&redirect_uri=${redirUrl}&scope=https%3A%2F%2Fgraph.microsoft.com%2FFiles.ReadWrite${ - UserSettings.items.oneDriveBusiness !== true ? ".AppFolder" : "" - }%20https%3A%2F%2Fgraph.microsoft.com%2FUser.Read%20offline_access&response_mode=query&prompt=consent`; - } chrome.identity.launchWebAuthFlow( { url: authUrl, interactive: true }, async (url) => { if (!url) { + chrome.runtime + .sendMessage({ action: "onedriveauthdone" }) + .catch(() => undefined); return; } - let hashMatches = url.split("#"); - if (service === "drive") { - hashMatches = url.slice(0, -1).split("?"); - } else if (service === "onedrive") { - hashMatches = url.split("?"); - } - + const hashMatches = url.split("?"); if (hashMatches.length < 2) { return; } - const hash = hashMatches[1]; - const resData = hash.split("&"); for (let i = 0; i < resData.length; i++) { const kv = resData[i]; @@ -318,59 +433,8 @@ function getBackupToken(service: string) { } const key = kvMatches[1]; const value = kvMatches[2]; - if (key === "access_token") { - if (service === "dropbox") { - UserSettings.items.dropboxToken = value; - UserSettings.commitItems(); - uploadBackup("dropbox"); - return; - } - } else if (key === "code") { - if (service === "drive") { - let success = false; - - const response = await fetch( - "https://www.googleapis.com/oauth2/v4/token?client_id=" + - getCredentials().drive.client_id + - "&client_secret=" + - getCredentials().drive.client_secret + - "&code=" + - value + - "&redirect_uri=" + - redirUrl + - "&grant_type=authorization_code", - { - method: "POST", - headers: { - Accept: "application/json", - "Content-Type": "application/x-www-form-urlencoded", - }, - } - ); - - try { - const res = await response.json(); - - if (res.error) { - console.error(res.error_description); - } else { - UserSettings.items.driveToken = res.access_token; - UserSettings.items.driveRefreshToken = res.refresh_token; - UserSettings.commitItems(); - success = true; - } - } catch (error) { - console.error(error); - throw error; - } - - uploadBackup("drive"); - return success; - } else if (service === "onedrive") { - // Need to trade code we got from launchWebAuthFlow for a - // token & refresh token - let success = false; - + if (key === "code") { + try { const response = await fetch( "https://login.microsoftonline.com/common/oauth2/v2.0/token", { @@ -379,32 +443,35 @@ function getBackupToken(service: string) { Accept: "application/json", "Content-Type": "application/x-www-form-urlencoded", }, + body: + "client_id=" + + getCredentials().onedrive.client_id + + "&client_secret=" + + getCredentials().onedrive.client_secret + + "&code=" + + value + + "&redirect_uri=" + + redirUrl + + "&grant_type=authorization_code", } ); - - try { - const res = await response.json(); - if (res.error) { - console.error(res.error_description); - } else { - UserSettings.items.oneDriveToken = res.access_token; - UserSettings.items.oneDriveRefreshToken = res.refresh_token; - UserSettings.commitItems(); - success = true; - } - } catch (error) { - console.error(error); - throw error; + const res = await response.json(); + if (res.error) { + console.error(res.error_description); + } else { + UserSettings.items.oneDriveToken = res.access_token; + UserSettings.items.oneDriveRefreshToken = res.refresh_token; + UserSettings.commitItems(); + uploadBackup("onedrive"); } - - uploadBackup("onedrive"); - return success; + } catch (error) { + console.error(error); + throw error; } + return; } } } - - return; } ); } @@ -419,10 +486,6 @@ async function uploadBackup(service: string) { await new Dropbox().upload(encryption); break; - case "drive": - await new Drive().upload(encryption); - break; - case "onedrive": await new OneDrive().upload(encryption); break; @@ -469,14 +532,14 @@ chrome.commands.onCommand.addListener(async (command: string) => { if (okToInjectContentScript(tab)) { await chrome.scripting.executeScript({ target: { tabId: tab.id }, - files: ["/dist/content.js"], + files: ["/js/content.js"], }); await chrome.scripting.insertCSS({ target: { tabId: tab.id }, files: ["/css/content.css"], }); - contentTab = tab; + chrome.storage.session.set({ captureTabId: tab.id }); chrome.tabs.sendMessage(tab.id, { action: "capture" }); } break; @@ -486,23 +549,23 @@ chrome.commands.onCommand.addListener(async (command: string) => { if (okToInjectContentScript(tab)) { await chrome.scripting.executeScript({ target: { tabId: tab.id }, - files: ["/dist/content.js"], + files: ["/js/content.js"], }); await chrome.scripting.insertCSS({ target: { tabId: tab.id }, files: ["/css/content.css"], }); - contentTab = tab; - const siteName = await getSiteName(); const entries = await EntryStorage.get(); - const matchedEntries = getMatchedEntries(siteName, entries); + // strict=true: autofill pastes a live OTP, so don't trust the page title + const matchedEntries = getMatchedEntries(siteName, entries, true); if (matchedEntries && matchedEntries.length === 1) { const entry = matchedEntries[0]; const encryption = new Encryption(cachedPassphrase, cachedKeyId); - entry.applyEncryption(encryption); + // applyEncryption is async now; await so entry.code is decrypted + await entry.applyEncryption(encryption); if ( entry.code !== CodeState.Encrypted && diff --git a/src/components/Import.vue b/src/components/Import.vue index e224a82a2..f6e51b71f 100644 --- a/src/components/Import.vue +++ b/src/components/Import.vue @@ -1,38 +1,95 @@
-
-
- - - - - - +
+
+
+ +

{{ i18n.import_backup }}

-
-

- {{ i18n.otp_backup_inform }} - {{ - i18n.otp_backup_learn - }} -

+

+ {{ i18n.otp_backup_inform }} + {{ + i18n.otp_backup_learn + }} +

+ +
+ + + +
+ +
+
-
{{ i18n.import_error_password }} @@ -40,12 +97,12 @@
diff --git a/src/components/Import/QrImport.vue b/src/components/Import/QrImport.vue index 6edd0702d..f3ca4a249 100644 --- a/src/components/Import/QrImport.vue +++ b/src/components/Import/QrImport.vue @@ -13,7 +13,7 @@
diff --git a/src/components/Popup/AddAccountPage.vue b/src/components/Popup/AddAccountPage.vue index 20e82e9bc..794adb6bd 100644 --- a/src/components/Popup/AddAccountPage.vue +++ b/src/components/Popup/AddAccountPage.vue @@ -1,57 +1,91 @@ diff --git a/src/components/Popup/DrivePage.vue b/src/components/Popup/DrivePage.vue deleted file mode 100644 index 351a61bbf..000000000 --- a/src/components/Popup/DrivePage.vue +++ /dev/null @@ -1,135 +0,0 @@ - - diff --git a/src/components/Popup/DropboxPage.vue b/src/components/Popup/DropboxPage.vue index 030d2d6ca..3f11e5491 100644 --- a/src/components/Popup/DropboxPage.vue +++ b/src/components/Popup/DropboxPage.vue @@ -1,45 +1,129 @@ diff --git a/src/components/Popup/EnterPasswordPage.vue b/src/components/Popup/EnterPasswordPage.vue index ebe533e86..24a7032ad 100644 --- a/src/components/Popup/EnterPasswordPage.vue +++ b/src/components/Popup/EnterPasswordPage.vue @@ -1,23 +1,43 @@ diff --git a/src/components/Popup/MainBody.vue b/src/components/Popup/MainBody.vue index 4e8c4e92d..7a99fe634 100644 --- a/src/components/Popup/MainBody.vue +++ b/src/components/Popup/MainBody.vue @@ -1,11 +1,29 @@ diff --git a/src/components/Popup/MainHeader.vue b/src/components/Popup/MainHeader.vue index f97d77817..c77b3f24b 100644 --- a/src/components/Popup/MainHeader.vue +++ b/src/components/Popup/MainHeader.vue @@ -1,7 +1,33 @@ diff --git a/src/components/Popup/OneDrivePage.vue b/src/components/Popup/OneDrivePage.vue index 8c93fc782..9fcc175cd 100644 --- a/src/components/Popup/OneDrivePage.vue +++ b/src/components/Popup/OneDrivePage.vue @@ -1,7 +1,10 @@ diff --git a/src/components/common/ButtonLink.vue b/src/components/common/ButtonLink.vue index 6a8753caf..98de8940c 100644 --- a/src/components/common/ButtonLink.vue +++ b/src/components/common/ButtonLink.vue @@ -9,9 +9,9 @@ diff --git a/src/components/common/FileInput.vue b/src/components/common/FileInput.vue index 9103990f0..91a44349f 100644 --- a/src/components/common/FileInput.vue +++ b/src/components/common/FileInput.vue @@ -1,19 +1,45 @@ diff --git a/src/components/common/SelectInput.vue b/src/components/common/SelectInput.vue index b249ddf9e..64a7a6bff 100644 --- a/src/components/common/SelectInput.vue +++ b/src/components/common/SelectInput.vue @@ -3,9 +3,9 @@
diff --git a/src/content.ts b/src/content.ts index 5ac1805cf..d4c5c5695 100644 --- a/src/content.ts +++ b/src/content.ts @@ -61,9 +61,10 @@ if (!document.getElementById("__ga_grayLayout__")) { // invalid command, ignore it break; } - - // https://stackoverflow.com/a/56483156 - return true; + // Only "capture" responds, and it does so synchronously, so don't return + // true. Returning true kept the channel open waiting for a response that + // never came for the other actions (e.g. sendCaptureUrl on a non-QR image), + // making the background sender's sendMessage promise reject. }); } @@ -98,6 +99,10 @@ function showGrayLayout() { event.preventDefault(); return; }; + // Belt-and-suspenders: explicitly refuse native drag (e.g. dragging over an + // image/link under the overlay), which otherwise shows the no-drop cursor. + grayLayout.ondragstart = () => false; + grayLayout.style.userSelect = "none"; } grayLayout.style.display = "block"; } @@ -112,6 +117,10 @@ function grayLayoutDown(event: MouseEvent) { return; } + // Stop the browser from starting a native text-selection / image drag, which + // shows the "no-drop" cursor and steals the gesture from our drag-select. + event.preventDefault(); + sessionStorage.setItem("captureBoxPositionLeft", event.clientX.toString()); sessionStorage.setItem("captureBoxPositionTop", event.clientY.toString()); captureBox.style.left = event.clientX + "px"; @@ -132,6 +141,12 @@ function grayLayoutMove(event: MouseEvent) { event.preventDefault(); return; } + // Only redraw while the left button is actually held. Without this the box + // tracked every bare pointer move (before the first click and after release), + // so the selection felt jumpy and "undraggable". + if (event.buttons !== 1) { + return; + } const captureBox = document.getElementById("__ga_captureBox__"); if (!captureBox) { return; @@ -223,22 +238,37 @@ async function qrDecode( ) as HTMLCanvasElement; const qr = new Image(); qr.onload = () => { + // A failed/empty capture yields a 0x0 image; bail with feedback rather than + // dividing by it and extracting a 0-size region. + if (!qr.width || !qr.height) { + alert(chrome.i18n.getMessage("errorqr")); + return; + } const devicePixelRatio = qr.width / window.innerWidth; canvas.width = qr.width; canvas.height = qr.height; - canvas.getContext("2d")?.drawImage(qr, 0, 0); - const imageData = canvas - .getContext("2d") - ?.getImageData( - left * devicePixelRatio, - top * devicePixelRatio, - width * devicePixelRatio, - height * devicePixelRatio - ); + // willReadFrequently: we call getImageData below; silences a Chrome perf hint + const ctx = canvas.getContext("2d", { willReadFrequently: true }); + if (!ctx) { + return; + } + ctx.drawImage(qr, 0, 0); + // Clamp the selection to the captured image and require a non-empty area. + // A click or 1px drag makes a zero/out-of-bounds region, and getImageData + // then throws an uncaught IndexSizeError that kills the scan silently. + const sx = Math.max(0, Math.floor(left * devicePixelRatio)); + const sy = Math.max(0, Math.floor(top * devicePixelRatio)); + const sw = Math.min(qr.width - sx, Math.floor(width * devicePixelRatio)); + const sh = Math.min(qr.height - sy, Math.floor(height * devicePixelRatio)); + if (sw <= 0 || sh <= 0) { + alert(chrome.i18n.getMessage("errorqr")); + return; + } + const imageData = ctx.getImageData(sx, sy, sw, sh); if (imageData) { canvas.width = imageData.width; canvas.height = imageData.height; - canvas.getContext("2d")?.putImageData(imageData, 0, 0); + ctx?.putImageData(imageData, 0, 0); const qrReader = new QRCode(); qrReader.callback = ( @@ -255,7 +285,8 @@ async function qrDecode( ) => { let qrRes = ""; if (error) { - console.error(error); + // qrcode-reader reports "no finder patterns" for any non-QR region; + // that's expected, so don't log it as an error -- fall back to jsQR. const jsQrCode = jsQR( imageData.data, imageData.width, @@ -279,18 +310,29 @@ async function qrDecode( qrReader.decode(imageData); } }; + qr.onerror = () => { + alert(chrome.i18n.getMessage("errorqr")); + }; qr.src = url; } +// Skip inputs the user can't see (hidden honeypots, off-screen fields) so the +// code doesn't land in the wrong box. checkVisibility is guarded for older +// browsers that don't support it. (#1273, #1136) +function isVisibleInput(input: HTMLInputElement) { + return typeof input.checkVisibility !== "function" || input.checkVisibility(); +} + function pasteCode(code: string) { const _inputBoxes = document.getElementsByTagName("input"); const inputBoxes: HTMLInputElement[] = []; for (let i = 0; i < _inputBoxes.length; i++) { if ( - _inputBoxes[i].type === "text" || - _inputBoxes[i].type === "number" || - _inputBoxes[i].type === "tel" || - _inputBoxes[i].type === "password" + (_inputBoxes[i].type === "text" || + _inputBoxes[i].type === "number" || + _inputBoxes[i].type === "tel" || + _inputBoxes[i].type === "password") && + isVisibleInput(_inputBoxes[i]) ) { inputBoxes.push(_inputBoxes[i]); } diff --git a/src/definitions/module-interface.d.ts b/src/definitions/module-interface.d.ts index 494266315..007de404b 100644 --- a/src/definitions/module-interface.d.ts +++ b/src/definitions/module-interface.d.ts @@ -37,6 +37,7 @@ interface MenuState { smartFilter: boolean; enableContextMenu: boolean; theme: string; + onboardingComplete: boolean; backupDisabled: boolean; storageArea: "sync" | "local"; } diff --git a/src/definitions/otp.d.ts b/src/definitions/otp.d.ts index c55fb1486..6259ef9be 100644 --- a/src/definitions/otp.d.ts +++ b/src/definitions/otp.d.ts @@ -2,6 +2,7 @@ interface OTPEntryInterface { type: number; // OTPType index: number; issuer: string; + host: string; secret: string | null; account: string; hash: string; @@ -16,7 +17,7 @@ interface OTPEntryInterface { create(): Promise; update(): Promise; next(): Promise; - applyEncryption(encryption: EncryptionInterface): void; + applyEncryption(encryption: EncryptionInterface): Promise; changeEncryption(encryption: EncryptionInterface): void; delete(): Promise; generate(): void; @@ -24,9 +25,9 @@ interface OTPEntryInterface { } interface EncryptionInterface { - getEncryptedString(data: string): string; - decryptSecretString(entry: string): string | null; - decryptEncSecret(entry: OTPEntryInterface): RawOTPStorage | null; + getEncryptedString(data: string): Promise; + decryptSecretString(entry: string): Promise; + decryptEncSecret(entry: OTPEntryInterface): Promise; getEncryptionStatus(): boolean; updateEncryptionPassword(password: string): void; getEncryptionKeyId(): string; @@ -41,6 +42,7 @@ interface RawOTPStorage { hash: string; index: number; issuer?: string; + host?: string; secret: string; type: string; counter?: number; diff --git a/src/definitions/shims-vue.d.ts b/src/definitions/shims-vue.d.ts index bac18c752..e4c904ad6 100644 --- a/src/definitions/shims-vue.d.ts +++ b/src/definitions/shims-vue.d.ts @@ -1,11 +1,20 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ declare module "*.vue" { - import Vue from "vue"; - export default Vue; + import { DefineComponent } from "vue"; + const component: DefineComponent< + Record, + Record, + any + >; + export default component; } declare module "*.svg" { - import { ComponentOptions } from "vue"; - const a: ComponentOptions; - export default a; + import { DefineComponent } from "vue"; + const component: DefineComponent< + Record, + Record, + any + >; + export default component; } diff --git a/src/definitions/vue.d.ts b/src/definitions/vue.d.ts index 236017a15..6dfd192b0 100644 --- a/src/definitions/vue.d.ts +++ b/src/definitions/vue.d.ts @@ -1,11 +1,10 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { Store } from "vuex"; -declare module "vue/types/vue" { - interface Vue { +declare module "@vue/runtime-core" { + interface ComponentCustomProperties { // Only in Popup $store: Store; - $dragula: any; // Only in Import $entries: OTPEntryInterface[]; $encryption: EncryptionInterface; diff --git a/src/definitions/vue2-dragula.d.ts b/src/definitions/vue2-dragula.d.ts deleted file mode 100644 index 9d122aa79..000000000 --- a/src/definitions/vue2-dragula.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -declare module "vue2-dragula" { - import { PluginFunction, VueConstructor } from "vue"; - - const Vue2Dragula: PluginFunction; -} diff --git a/src/import.ts b/src/import.ts index 21bebdabb..dccca0db5 100644 --- a/src/import.ts +++ b/src/import.ts @@ -1,48 +1,51 @@ -import Vue from "vue"; +import { createApp } from "vue"; import ImportView from "./components/Import.vue"; import CommonComponents from "./components/common/index"; import { loadI18nMessages } from "./store/i18n"; -import { Encryption } from "./models/encryption"; +import { Encryption, decryptString } from "./models/encryption"; import { EntryStorage } from "./models/storage"; import { getOTPAuthPerLineFromOPTAuthMigration } from "./models/migration"; -import * as CryptoJS from "crypto-js"; +import { argonHash, argonVerify } from "./models/password"; async function init() { - // i18n - Vue.prototype.i18n = await loadI18nMessages(); + try { + const app = createApp(ImportView); + // i18n + app.config.globalProperties.i18n = await loadI18nMessages(); - // Load common components globally - for (const component of CommonComponents) { - Vue.component(component.name, component.component); - } + // Load common components globally + for (const component of CommonComponents) { + app.component(component.name, component.component); + } - // Load entries to global - const cachedSecrets = await getCachedSecrets(); - const encryption = new Encryption( - cachedSecrets.cachedPassphrase, - cachedSecrets.cachedKeyId - ); - const entries = await EntryStorage.get(); + // Load entries to global + const cachedSecrets = await getCachedSecrets(); + const encryption = new Encryption( + cachedSecrets.cachedPassphrase, + cachedSecrets.cachedKeyId + ); + const entries = await EntryStorage.get(); - if (encryption.getEncryptionStatus()) { - for (const entry of entries) { - await entry.applyEncryption(encryption); + if (encryption.getEncryptionStatus()) { + for (const entry of entries) { + await entry.applyEncryption(encryption); + } } - } - Vue.prototype.$entries = entries; - Vue.prototype.$encryption = encryption; + app.config.globalProperties.$entries = entries; + app.config.globalProperties.$encryption = encryption; - const instance = new Vue({ - render: (h) => h(ImportView), - }).$mount("#import"); + const instance = app.mount("#import"); - // Set title - try { - document.title = instance.i18n.extName; + // Set title + try { + document.title = instance.i18n.extName; + } catch (e) { + console.error(e); + } } catch (e) { - console.error(e); + console.error("Import page init failed:", e); } } @@ -90,13 +93,24 @@ export async function decryptBackupData( continue; } + // decryptString is prefix-aware (new AES-GCM or legacy AES-CBC backups) + const decryptedJson = await decryptString( + unknownStorageItem.data, + decryptKey + ); + if (!decryptedJson) { + // a single corrupt/undecryptable entry must not abort the whole import + continue; + } + let decryptedData; + try { + decryptedData = JSON.parse(decryptedJson); + } catch { + continue; + } storageItem = { ...unknownStorageItem, - ...JSON.parse( - CryptoJS.AES.decrypt(unknownStorageItem.data, decryptKey).toString( - CryptoJS.enc.Utf8 - ) - ), + ...decryptedData, encrypted: false, }; } else { @@ -109,15 +123,15 @@ export async function decryptBackupData( continue; } if (storageItem.encrypted && passphrase) { - try { - storageItem.secret = CryptoJS.AES.decrypt( - storageItem.secret, - passphrase - ).toString(CryptoJS.enc.Utf8); - storageItem.encrypted = false; - } catch (error) { + const decryptedSecret = await decryptString( + storageItem.secret, + passphrase + ); + if (!decryptedSecret) { continue; } + storageItem.secret = decryptedSecret; + storageItem.encrypted = false; } // storageItem.secret may be empty after decrypt with wrong // passphrase @@ -143,47 +157,17 @@ async function findAndUnlockKey( return null; } - const rawHash = await new Promise((resolve: (value: string) => void) => { - const iframe = document.getElementById("argon-sandbox"); - const message = { - action: "hash", - value: password, - salt: key.salt, - }; - if (iframe) { - window.addEventListener("message", (response) => { - resolve(response.data.response); - }); - // @ts-expect-error bad typings - iframe.contentWindow.postMessage(message, "*"); - } - }); + const rawHash = await argonHash(password, key.salt); // https://passlib.readthedocs.io/en/stable/lib/passlib.hash.argon2.html#format-algorithm - const possibleHash = rawHash.split("$")[5]; + const possibleHash = rawHash ? rawHash.split("$")[5] : ""; if (!possibleHash) { throw new Error("argon2 did not return a hash!"); } // verify user password by comparing their password hash with the // hash of their password's hash - const isCorrectPassword = await new Promise( - (resolve: (value: string) => void) => { - const iframe = document.getElementById("argon-sandbox"); - const message = { - action: "verify", - value: possibleHash, - hash: key.hash, - }; - if (iframe) { - window.addEventListener("message", (response) => { - resolve(response.data.response); - }); - // @ts-expect-error bad typings - iframe.contentWindow.postMessage(message, "*"); - } - } - ); + const isCorrectPassword = await argonVerify(possibleHash, key.hash); if (!isCorrectPassword) { return null; @@ -200,7 +184,13 @@ export async function getEntryDataFromOTPAuthPerLine(importCode: string) { for (let item of lines) { item = item.trim(); if (item.startsWith("otpauth-migration:")) { - const migrationData = getOTPAuthPerLineFromOPTAuthMigration(item); + let migrationData: string[] = []; + try { + migrationData = getOTPAuthPerLineFromOPTAuthMigration(item); + } catch (error) { + // one malformed migration payload must not abort the whole batch + console.warn("Failed to parse migration payload", error); + } for (const line of migrationData) { lines.push(line); } @@ -210,15 +200,28 @@ export async function getEntryDataFromOTPAuthPerLine(importCode: string) { continue; } - let uri = item.split("otpauth://")[1]; - let type = uri.substr(0, 4).toLowerCase(); - uri = uri.substr(5); - let label = uri.split("?")[0]; - const parameterPart = uri.split("?")[1]; + // "otpauth://" is not a special scheme in the WHATWG URL Standard, so + // browsers are NOT required to (and in practice don't consistently) + // parse the "//type/label" part into url.host/url.pathname — Chrome 126 + // leaves url.host empty and dumps everything into pathname, while Node's + // URL parses host as "type". Only the "?query" part is parsed reliably + // everywhere, so the type/label split stays manual and only parameter + // parsing below is upgraded to URLSearchParams. + const afterScheme = item.split("otpauth://")[1]; + if (!afterScheme) { + // malformed URI (e.g. missing "//") must not abort the batch + failedCount++; + continue; + } + let type = afterScheme.substr(0, 4).toLowerCase(); + const rest = afterScheme.substr(5); + let label = rest.split("?")[0]; + const parameterPart = rest.split("?")[1]; if (!parameterPart) { failedCount++; continue; } else { + const params = new URLSearchParams(parameterPart); let secret = ""; let account: string | undefined; let issuer: string | undefined; @@ -237,36 +240,34 @@ export async function getEntryDataFromOTPAuthPerLine(importCode: string) { } else { account = label; } - const parameters = parameterPart.split("&"); - parameters.forEach((item) => { - const parameter = item.split("="); - if (parameter[0].toLowerCase() === "secret") { - secret = parameter[1]; - } else if (parameter[0].toLowerCase() === "issuer") { - try { - issuer = decodeURIComponent(parameter[1]); - } catch { - issuer = parameter[1]; - } - issuer = issuer.replace(/\+/g, " "); - } /* else if (parameter[0].toLowerCase() === "counter") { - let counter = Number(parameter[1]); - counter = isNaN(counter) || counter < 0 ? 0 : counter; - } */ else if ( - parameter[0].toLowerCase() === "period" - ) { - period = Number(parameter[1]); - period = - isNaN(period) || period < 0 || period > 60 || 60 % period !== 0 - ? undefined - : period; - } else if (parameter[0].toLowerCase() === "digits") { - digits = Number(parameter[1]); - digits = isNaN(digits) ? 6 : digits; - } else if (parameter[0].toLowerCase() === "algorithm") { - algorithm = parameter[1]; - } - }); + + // secret must be read as the raw parameter value: URLSearchParams + // already decodes "+" as a space for every field, which matches the + // old manual issuer handling but NOT the old secret handling (the old + // code never unescaped "+" in secret). Base32/hex secrets never + // contain "+", so this only matters for malformed input, which falls + // through to the format check below and is rejected the same way. + secret = params.get("secret") || ""; + if (params.has("issuer")) { + // URLSearchParams already decodes "+" as a space, matching the old + // manual decodeURIComponent + replace(/\+/g, " ") behavior. + issuer = params.get("issuer") || ""; + } + /* counter is intentionally not parsed here, matching prior behavior */ + if (params.has("period")) { + period = Number(params.get("period")); + // accept any positive integer period; the old "> 60" / "60 % period" + // checks silently dropped valid periods (45, 60, 90, 120...) so those + // OTPs fell back to 30s and produced wrong codes (#1271, #1508) + period = !Number.isInteger(period) || period < 1 ? undefined : period; + } + if (params.has("digits")) { + digits = Number(params.get("digits")); + digits = isNaN(digits) ? 6 : digits; + } + if (params.has("algorithm")) { + algorithm = params.get("algorithm") || undefined; + } if (!secret) { failedCount++; diff --git a/src/models/advisor.ts b/src/models/advisor.ts index c729fde24..8cf238cc9 100644 --- a/src/models/advisor.ts +++ b/src/models/advisor.ts @@ -14,7 +14,7 @@ export class AdvisorInsight implements AdvisorInsightInterface { constructor(insight: AdvisorInsightInterface) { this.id = insight.id; - this.level = insight.level as InsightLevel; + this.level = insight.level; this.levelText = chrome.i18n.getMessage(insight.level); this.description = insight.description; this.link = insight.link; diff --git a/src/models/backup.ts b/src/models/backup.ts index 5484c5727..49dead367 100644 --- a/src/models/backup.ts +++ b/src/models/backup.ts @@ -2,476 +2,180 @@ import { getCredentials } from "./credentials"; import { Encryption } from "./encryption"; import { UserSettings } from "./settings"; import { EntryStorage } from "./storage"; +import { cloudBackupAllowed } from "../utils"; export class Dropbox implements BackupProvider { - private async getToken() { - await UserSettings.updateItems(); - return UserSettings.items.dropboxToken || ""; - } - - async upload(encryption: Encryption) { + private async refreshToken(): Promise { await UserSettings.updateItems(); - - if (UserSettings.items.dropboxEncrypted === undefined) { - // Encrypt by default if user hasn't set yet - UserSettings.items.dropboxEncrypted = true; + if (!UserSettings.items.dropboxRefreshToken) { + UserSettings.items.dropboxRevoked = true; UserSettings.commitItems(); + return false; } - const exportData = await EntryStorage.backupGetExport( - encryption, - UserSettings.items.dropboxEncrypted === true - ); - const backup = JSON.stringify(exportData, null, 2); - - const url = "https://content.dropboxapi.com/2/files/upload"; - const token = await this.getToken(); - return new Promise( - (resolve: (value: boolean) => void, reject: (reason: Error) => void) => { - if (!token) { - return resolve(false); - } - try { - const xhr = new XMLHttpRequest(); - const now = new Date().toISOString().slice(0, 10).replace(/-/g, ""); - const apiArg = { - path: `/${now}.json`, - mode: "add", - autorename: true, - }; - xhr.open("POST", url); - xhr.setRequestHeader("Authorization", "Bearer " + token); - xhr.setRequestHeader("Content-type", "application/octet-stream"); - xhr.setRequestHeader("Dropbox-API-Arg", JSON.stringify(apiArg)); - xhr.onreadystatechange = () => { - if (xhr.readyState === 4) { - if (xhr.status === 401) { - UserSettings.items.dropboxToken = undefined; - UserSettings.items.dropboxRevoked = true; - UserSettings.commitItems(); - return resolve(false); - } - try { - const res = JSON.parse(xhr.responseText); - if (res.name) { - resolve(true); - } else { - resolve(false); - } - } catch (error) { - reject(error as Error); - } - } - return; - }; - xhr.send(backup); - } catch (error) { - return reject(error as Error); - } - } - ); - } - async getUser() { - await UserSettings.updateItems(); - const url = "https://api.dropboxapi.com/2/users/get_current_account"; - const token = await this.getToken(); - return new Promise((resolve: (value: string) => void) => { - if (!token) { - return resolve("Error: No token"); - } - const xhr = new XMLHttpRequest(); - xhr.open("POST", url); - xhr.setRequestHeader("Authorization", "Bearer " + token); - xhr.onreadystatechange = () => { - if (xhr.readyState === 4) { - if (xhr.status === 401) { - UserSettings.items.dropboxToken = undefined; - UserSettings.items.dropboxRevoked = true; - UserSettings.commitItems(); - resolve( - "Error: Response was 401. You will be logged out the next time you open Authenticator." - ); - } - try { - const res = JSON.parse(xhr.responseText); - if (res.email) { - resolve(res.email); - } else { - console.error("Could not find email in response.", res); - resolve("Error: res.email was undefined."); - } - } catch (e) { - console.error(e); - resolve("Error"); - } - } - return; - }; - xhr.send(null); + // PKCE-upgraded flow: refresh without client_secret + const res = await fetch("https://api.dropboxapi.com/oauth2/token", { + method: "POST", + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + body: + "grant_type=refresh_token" + + "&refresh_token=" + + encodeURIComponent(UserSettings.items.dropboxRefreshToken) + + "&client_id=" + + encodeURIComponent(getCredentials().dropbox.client_id), }); - } -} - -export class Drive implements BackupProvider { - private async getToken() { - await UserSettings.updateItems(); - if ( - !UserSettings.items.driveToken || - (await new Promise( - ( - resolve: (value: boolean) => void, - reject: (reason: Error) => void - ) => { - const xhr = new XMLHttpRequest(); - xhr.open("GET", "https://www.googleapis.com/drive/v3/files"); - xhr.setRequestHeader( - "Authorization", - "Bearer " + UserSettings.items.driveToken - ); - xhr.onreadystatechange = async () => { - if (xhr.readyState === 4) { - try { - const res = JSON.parse(xhr.responseText); - if (res.error) { - if (res.error.code === 401) { - if ( - navigator.userAgent.indexOf("Chrome") !== -1 && - navigator.userAgent.indexOf("OPR") === -1 && - navigator.userAgent.indexOf("Edg") === -1 - ) { - // Clear invalid token from - // chrome://identity-internals/ - await chrome.identity.removeCachedAuthToken({ - token: UserSettings.items.driveToken as string, - }); - } - UserSettings.items.driveToken = undefined; - UserSettings.commitItems(); - resolve(true); - } - } else { - resolve(false); - } - } catch (error) { - console.error(error); - reject(error as Error); - } - } - return; - }; - xhr.send(); - } - )) - ) { - await this.refreshToken(); + if (res.status === 401) { + UserSettings.items.dropboxRefreshToken = undefined; + UserSettings.items.dropboxRevoked = true; + UserSettings.commitItems(); + return false; } - return UserSettings.items.driveToken; + const data = await res.json(); + if (data.error) { + if (data.error === "invalid_grant") { + UserSettings.items.dropboxRefreshToken = undefined; + UserSettings.items.dropboxRevoked = true; + UserSettings.commitItems(); + } + console.error(data.error_description); + return false; + } + UserSettings.items.dropboxToken = data.access_token; + UserSettings.commitItems(); + return true; } - private async refreshToken() { + private async getToken(): Promise { await UserSettings.updateItems(); - - if ( - navigator.userAgent.indexOf("Chrome") !== -1 && - navigator.userAgent.indexOf("OPR") === -1 && - navigator.userAgent.indexOf("Edg") === -1 - ) { - return new Promise((resolve: (value: boolean) => void) => { - return chrome.identity.getAuthToken( - { - interactive: false, - scopes: ["https://www.googleapis.com/auth/drive.file"], - }, - (token) => { - UserSettings.items.driveToken = token; - if (!token) { - UserSettings.items.driveRevoked = true; - } - UserSettings.commitItems(); - resolve(Boolean(token)); - } - ); - }); - } else { - return new Promise( - ( - resolve: (value: boolean) => void, - reject: (reason: Error) => void - ) => { - const xhr = new XMLHttpRequest(); - xhr.open( - "POST", - "https://www.googleapis.com/oauth2/v4/token?client_id=" + - getCredentials().drive.client_id + - "&client_secret=" + - getCredentials().drive.client_secret + - "&refresh_token=" + - UserSettings.items.driveRefreshToken + - "&grant_type=refresh_token" - ); - xhr.setRequestHeader("Accept", "application/json"); - xhr.onreadystatechange = () => { - if (xhr.readyState === 4) { - if (xhr.status === 401) { - UserSettings.items.driveRefreshToken = undefined; - UserSettings.items.driveRevoked = true; - UserSettings.commitItems(); - return resolve(false); - } - try { - const res = JSON.parse(xhr.responseText); - if (res.error) { - if (res.error === "invalid_grant") { - UserSettings.items.driveRefreshToken = undefined; - UserSettings.items.driveRevoked = true; - UserSettings.commitItems(); - } - console.error(res.error_description); - resolve(false); - } else { - UserSettings.items.driveToken = res.access_token; - UserSettings.commitItems(); - resolve(true); - } - } catch (error) { - console.error(error); - reject(error as Error); - } - } - return; - }; - xhr.send(); - } - ); + if (!UserSettings.items.dropboxToken) { + const refreshed = await this.refreshToken(); + if (!refreshed) { + return ""; + } } + return UserSettings.items.dropboxToken || ""; } - private async getFolder() { - const token = await this.getToken(); - if (!token) { + async upload(encryption: Encryption) { + // Never let a cloud backup carry plaintext secrets: require a master + // password (which encrypts the export) before uploading anything. + if (!cloudBackupAllowed(encryption)) { return false; } await UserSettings.updateItems(); - if (UserSettings.items.driveFolder) { - await new Promise( - ( - resolve: (value: boolean) => void, - reject: (reason: Error) => void - ) => { - const xhr = new XMLHttpRequest(); - xhr.open( - "GET", - "https://www.googleapis.com/drive/v3/files/" + - UserSettings.items.driveFolder + - "?fields=trashed" - ); - xhr.setRequestHeader("Authorization", "Bearer " + token); - xhr.setRequestHeader("Accept", "application/json"); - xhr.onreadystatechange = () => { - if (xhr.readyState === 4) { - if (xhr.status === 401) { - UserSettings.items.driveToken = undefined; - UserSettings.commitItems(); - return resolve(false); - } - try { - const res = JSON.parse(xhr.responseText); - if (res.error) { - if (res.error.code === 404) { - UserSettings.items.driveFolder = undefined; - UserSettings.commitItems(); - resolve(true); - } else { - console.error(res.error.message); - resolve(false); - } - } else if (res.trashed) { - UserSettings.items.driveFolder = undefined; - UserSettings.commitItems(); - resolve(true); - } else { - resolve(true); - } - } catch (error) { - console.error(error); - reject(error as Error); - } - } - return; - }; - xhr.send(); - } - ); - } - if (!UserSettings.items.driveFolder) { - await new Promise( - ( - resolve: (value: boolean) => void, - reject: (reason: Error) => void - ) => { - // create folder - const xhr = new XMLHttpRequest(); - xhr.open("POST", "https://www.googleapis.com/drive/v3/files/"); - xhr.setRequestHeader("Authorization", "Bearer " + token); - xhr.setRequestHeader("Accept", "application/json"); - xhr.setRequestHeader("Content-Type", "application/json"); - xhr.onreadystatechange = () => { - if (xhr.readyState === 4) { - if (xhr.status === 401) { - UserSettings.items.driveToken = undefined; - UserSettings.commitItems(); - return resolve(false); - } - try { - const res = JSON.parse(xhr.responseText); - if (!res.error) { - UserSettings.items.driveFolder = res.id; - UserSettings.commitItems(); - resolve(true); - } else { - console.error(res.error.message); - resolve(false); - } - } catch (error) { - console.error(error); - reject(error as Error); - } - } - return; - }; - xhr.send( - JSON.stringify({ - name: "Authenticator Backups", - mimeType: "application/vnd.google-apps.folder", - }) - ); - } - ); - } - return UserSettings.items.driveFolder; - } - async upload(encryption: Encryption) { - await UserSettings.updateItems(); - if (UserSettings.items.driveEncrypted === undefined) { - UserSettings.items.driveEncrypted = true; + if (UserSettings.items.dropboxEncrypted === undefined) { + // Encrypt by default if user hasn't set yet + UserSettings.items.dropboxEncrypted = true; UserSettings.commitItems(); } const exportData = await EntryStorage.backupGetExport( encryption, - UserSettings.items.driveEncrypted === true + UserSettings.items.dropboxEncrypted === true ); const backup = JSON.stringify(exportData, null, 2); + const url = "https://content.dropboxapi.com/2/files/upload"; const token = await this.getToken(); if (!token) { return false; } - const folderId = await this.getFolder(); - return new Promise( - (resolve: (value: boolean) => void, reject: (reason: Error) => void) => { - if (!token || !folderId) { - return resolve(false); - } - try { - const xhr = new XMLHttpRequest(); - const now = new Date().toISOString().slice(0, 10).replace(/-/g, ""); - xhr.open( - "POST", - "https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart" - ); - xhr.setRequestHeader("Authorization", "Bearer " + token); - xhr.setRequestHeader( - "Content-type", - "multipart/related; boundary=segment_marker" - ); - xhr.onreadystatechange = () => { - if (xhr.readyState === 4) { - if (xhr.status === 401) { - UserSettings.items.driveToken = undefined; - UserSettings.commitItems(); - return resolve(false); - } - try { - const res = JSON.parse(xhr.responseText); - if (!res.error) { - resolve(true); - } else { - console.error(res.error.message); - resolve(false); - } - } catch (error) { - reject(error as Error); - } - } - return; - }; - const requestDataPrototype = [ - "--segment_marker", - "Content-Type: application/json; charset=UTF-8", - "", - JSON.stringify({ - name: `${now}.json`, - parents: [UserSettings.items.driveFolder], - }), - "", - "--segment_marker", - "Content-Type: application/octet-stream", - "", - backup, - "--segment_marker--", - ]; - let requestData = ""; - requestDataPrototype.forEach((line) => { - requestData = requestData + line + "\n"; - }); - xhr.send(requestData); - } catch (error) { - return reject(error as Error); + const now = new Date().toISOString().slice(0, 10).replace(/-/g, ""); + const apiArg = { + path: `/${now}.json`, + mode: "add", + autorename: true, + }; + // fetch (not XMLHttpRequest) because upload runs in the MV3 background + // service worker, where XHR is not defined. + const res = await fetch(url, { + method: "POST", + headers: { + Authorization: "Bearer " + token, + "Content-Type": "application/octet-stream", + "Dropbox-API-Arg": JSON.stringify(apiArg), + }, + body: backup, + }); + if (res.status === 401) { + // Token expired: try refresh once, then retry + UserSettings.items.dropboxToken = undefined; + UserSettings.commitItems(); + const refreshed = await this.refreshToken(); + if (!refreshed) { + return false; + } + const newToken = UserSettings.items.dropboxToken || ""; + if (!newToken) { + return false; + } + const retryRes = await fetch(url, { + method: "POST", + headers: { + Authorization: "Bearer " + newToken, + "Content-Type": "application/octet-stream", + "Dropbox-API-Arg": JSON.stringify(apiArg), + }, + body: backup, + }); + if (!retryRes.ok) { + if (retryRes.status === 401) { + UserSettings.items.dropboxToken = undefined; + UserSettings.items.dropboxRevoked = true; + UserSettings.commitItems(); } + return false; } - ); + const retryBody = await retryRes.json(); + return Boolean(retryBody.name); + } + if (!res.ok) { + // a non-2xx (5xx, HTML error page, ...) is a failed upload, not + // something to JSON.parse and maybe misread as success. Surface + // Dropbox's body so the actual cause (bad path / arg / scope) is visible. + const detail = await res.text(); + throw new Error( + "Dropbox upload failed: HTTP " + res.status + " " + detail + ); + } + const body = await res.json(); + return Boolean(body.name); } async getUser() { + await UserSettings.updateItems(); + const url = "https://api.dropboxapi.com/2/users/get_current_account"; const token = await this.getToken(); if (!token) { - return "Error: Access revoked or expired."; + return "Error: No token"; } - - await UserSettings.updateItems(); - return new Promise((resolve: (value: string) => void) => { - if (!token) { - resolve("Error: Access revoked or expired."); - } - const xhr = new XMLHttpRequest(); - xhr.open("GET", "https://www.googleapis.com/drive/v2/about?fields=user"); - xhr.setRequestHeader("Authorization", "Bearer " + token); - xhr.onreadystatechange = () => { - if (xhr.readyState === 4) { - if (xhr.status === 401) { - UserSettings.items.driveToken = undefined; - UserSettings.commitItems(); - resolve( - "Error: Response was 401. You will be logged out the next time you open Authenticator." - ); - } - try { - const res = JSON.parse(xhr.responseText); - if (!res.error) { - resolve(res.user.emailAddress); - } else { - console.error(res.error.message); - resolve("Error"); - } - } catch (e) { - console.error(e); - resolve("Error"); - } - } - return; - }; - xhr.send(); + const res = await fetch(url, { + method: "POST", + headers: { + Authorization: "Bearer " + token, + "Content-Type": "application/json", + }, + body: "null", }); + if (res.status === 401) { + UserSettings.items.dropboxToken = undefined; + UserSettings.items.dropboxRevoked = true; + UserSettings.commitItems(); + return "Error: Response was 401. You will be logged out the next time you open OTPilot."; + } + try { + const data = await res.json(); + if (data.email) { + return data.email as string; + } else { + console.error("Could not find email in response.", data); + return "Error: res.email was undefined."; + } + } catch (e) { + console.error(e); + return "Error"; + } } } @@ -503,6 +207,8 @@ export class OneDrive implements BackupProvider { UserSettings.items.oneDriveToken = undefined; UserSettings.commitItems(); resolve(true); + } else { + resolve(false); } } else { resolve(false); @@ -580,9 +286,13 @@ export class OneDrive implements BackupProvider { } async upload(encryption: Encryption) { + if (!cloudBackupAllowed(encryption)) { + return false; + } await UserSettings.updateItems(); if (UserSettings.items.oneDriveEncrypted === undefined) { UserSettings.items.oneDriveEncrypted = true; + UserSettings.commitItems(); } const exportData = await EntryStorage.backupGetExport( encryption, @@ -612,7 +322,14 @@ export class OneDrive implements BackupProvider { xhr.onreadystatechange = () => { if (xhr.readyState === 4) { if (xhr.status === 401) { - UserSettings.removeItem("oneDriveToken"); + UserSettings.items.oneDriveToken = undefined; + UserSettings.items.oneDriveRevoked = true; + UserSettings.commitItems(); + return resolve(false); + } + if (xhr.status < 200 || xhr.status >= 300) { + // a non-2xx is a failed upload; don't fall through and risk + // misreading the body as success return resolve(false); } try { @@ -655,8 +372,9 @@ export class OneDrive implements BackupProvider { UserSettings.items.oneDriveToken = undefined; UserSettings.commitItems(); resolve( - "Error: Response was 401. You will be logged out the next time you open Authenticator." + "Error: Response was 401. You will be logged out the next time you open OTPilot." ); + return; } try { const res = JSON.parse(xhr.responseText); diff --git a/src/models/credentials.ts b/src/models/credentials.ts index 4c16f5621..d5c1b4e69 100644 --- a/src/models/credentials.ts +++ b/src/models/credentials.ts @@ -5,7 +5,7 @@ export function getCredentials() { client_secret: "", // Google client secret }, dropbox: { - client_id: "", // Dropbox client ID + client_id: "lmii0f2hnnztmvv", // Dropbox client ID }, onedrive: { client_id: "", // Microsoft Identity client ID diff --git a/src/models/encryption.ts b/src/models/encryption.ts index ebca10a85..aa074e06c 100644 --- a/src/models/encryption.ts +++ b/src/models/encryption.ts @@ -1,68 +1,150 @@ import * as CryptoJS from "crypto-js"; +// New ciphertext is authenticated AES-GCM via WebCrypto, tagged with this +// prefix so it is self-describing. Legacy ciphertext is crypto-js AES-CBC +// (OpenSSL "Salted__" framing, base64 starting "U2FsdGVkX1"); it stays readable +// for old storage and backups and upgrades to GCM the next time it is written. +const GCM_PREFIX = "v4:"; + +function bytesToBase64(bytes: Uint8Array): string { + let binary = ""; + for (let i = 0; i < bytes.length; i++) { + binary += String.fromCharCode(bytes[i]); + } + return btoa(binary); +} + +function base64ToBytes(base64: string): Uint8Array { + const binary = atob(base64); + const bytes = new Uint8Array(binary.length); + for (let i = 0; i < binary.length; i++) { + bytes[i] = binary.charCodeAt(i); + } + return bytes; +} + +async function deriveKey(password: string): Promise { + const raw = await crypto.subtle.digest( + "SHA-256", + new TextEncoder().encode(password) + ); + return crypto.subtle.importKey("raw", raw, "AES-GCM", false, [ + "encrypt", + "decrypt", + ]); +} + +// Prefix-aware decrypt shared by the Encryption class and the backup importer. +// GCM throws on a wrong key / tampered data (auth tag); we surface that as null +// like the legacy path. Returns null on any failure. +export async function decryptString( + data: string, + password: string +): Promise { + if (data.startsWith(GCM_PREFIX)) { + try { + const combined = base64ToBytes(data.slice(GCM_PREFIX.length)); + const iv = combined.slice(0, 12); + const ciphertext = combined.slice(12); + const key = await deriveKey(password); + const plaintext = await crypto.subtle.decrypt( + { name: "AES-GCM", iv }, + key, + ciphertext + ); + return new TextDecoder().decode(plaintext); + } catch (error) { + return null; + } + } + // legacy AES-CBC (crypto-js) + try { + const decrypted = CryptoJS.AES.decrypt(data, password).toString( + CryptoJS.enc.Utf8 + ); + return decrypted || null; + } catch (error) { + return null; + } +} + export class Encryption implements EncryptionInterface { private password: string; private keyId: string; + private keyPromise?: Promise; constructor(hash: string, keyId: string) { this.password = hash; this.keyId = keyId; + // Derive eagerly. These instances get stored in the reactive Vuex store + // (state.encryption / entry.encryption); if getKey() lazily assigned + // this.keyPromise later, that write would mutate reactive state during an + // await -- outside a mutation handler -- and trip Vuex strict mode. + if (hash) { + this.keyPromise = deriveKey(hash); + } } - getEncryptedString(data: string): string { + // Derive a 256-bit AES-GCM key from the (high-entropy argon2) saltedHash. + // Cached per instance. + private getKey(): Promise { + if (!this.keyPromise) { + this.keyPromise = deriveKey(this.password); + } + return this.keyPromise; + } + + async getEncryptedString(data: string): Promise { if (!this.password) { return data; - } else { - return CryptoJS.AES.encrypt(data, this.password).toString(); } + const key = await this.getKey(); + const iv = crypto.getRandomValues(new Uint8Array(12)); + const ciphertext = await crypto.subtle.encrypt( + { name: "AES-GCM", iv }, + key, + new TextEncoder().encode(data) + ); + const combined = new Uint8Array(iv.length + ciphertext.byteLength); + combined.set(iv, 0); + combined.set(new Uint8Array(ciphertext), iv.length); + return GCM_PREFIX + bytesToBase64(combined); } - decryptSecretString(secret: string) { - try { - const decryptedSecret = CryptoJS.AES.decrypt( - secret, - this.password - ).toString(CryptoJS.enc.Utf8); - - if (!decryptedSecret) { - return null; - } - - if (decryptedSecret.length < 8) { - return null; - } - - if ( - !/^[a-z2-7]+=*$/i.test(decryptedSecret) && - !/^[0-9a-f]+$/i.test(decryptedSecret) && - !/^blz-/.test(decryptedSecret) && - !/^bliz-/.test(decryptedSecret) && - !/^stm-/.test(decryptedSecret) - ) { - return null; - } - - return decryptedSecret; - } catch (error) { + async decryptSecretString(secret: string): Promise { + const decryptedSecret = await decryptString(secret, this.password); + if (!decryptedSecret) { return null; } - } - decryptEncSecret(entry: OTPEntryInterface) { - try { - if (!entry.encData) { - return null; - } + if (decryptedSecret.length < 8) { + return null; + } - const decryptedData = CryptoJS.AES.decrypt( - entry.encData, - this.password - ).toString(CryptoJS.enc.Utf8); + if ( + !/^[a-z2-7]+=*$/i.test(decryptedSecret) && + !/^[0-9a-f]+$/i.test(decryptedSecret) && + !/^blz-/.test(decryptedSecret) && + !/^bliz-/.test(decryptedSecret) && + !/^stm-/.test(decryptedSecret) + ) { + return null; + } - if (!decryptedData) { - return null; - } + return decryptedSecret; + } + async decryptEncSecret( + entry: OTPEntryInterface + ): Promise { + if (!entry.encData) { + return null; + } + const decryptedData = await decryptString(entry.encData, this.password); + if (!decryptedData) { + return null; + } + try { return JSON.parse(decryptedData); } catch (error) { return null; @@ -70,11 +152,12 @@ export class Encryption implements EncryptionInterface { } getEncryptionStatus(): boolean { - return this.password ? true : false; + return Boolean(this.password); } updateEncryptionPassword(password: string) { this.password = password; + this.keyPromise = undefined; } setEncryptionKeyId(id: string): void { diff --git a/src/models/key-utilities.ts b/src/models/key-utilities.ts index 31bcb0ba5..7482cddb5 100644 --- a/src/models/key-utilities.ts +++ b/src/models/key-utilities.ts @@ -21,21 +21,6 @@ export class KeyUtilities { return Number(`0x${s}`); } - private static hex2str(hex: string) { - let str = ""; - for (let i = 0; i < hex.length; i += 2) { - str += String.fromCharCode(this.hex2dec(hex.substr(i, 2))); - } - return str; - } - - private static leftpad(str: string, len: number, pad: string): string { - if (len + 1 >= str.length) { - str = new Array(len + 1 - str.length).join(pad) + str; - } - return str; - } - private static base32tohex(base32: string): string { const base32chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; let bits = ""; @@ -48,7 +33,10 @@ export class KeyUtilities { padding++; } else { const val = base32chars.indexOf(base32.charAt(i).toUpperCase()); - bits += this.leftpad(val.toString(2), 5, "0"); + if (val === -1) { + throw new Error("Invalid Base32 string"); + } + bits += val.toString(2).padStart(5, "0"); } } @@ -167,7 +155,7 @@ export class KeyUtilities { counter = Math.floor(epoch / period); } - const time = this.leftpad(this.dec2hex(counter), 16, "0"); + const time = this.dec2hex(counter).padStart(16, "0"); if (key.length % 2 === 1) { if (key.substr(-1) === "0") { diff --git a/src/models/migration.ts b/src/models/migration.ts index 9d00c63e8..4a222fe2d 100644 --- a/src/models/migration.ts +++ b/src/models/migration.ts @@ -85,7 +85,19 @@ export function getOTPAuthPerLineFromOPTAuthMigration(migrationUri: string) { return []; } - const base64Data = decodeURIComponent(migrationUri.split("data=")[1]); + const dataPart = migrationUri.split("data=")[1]; + if (!dataPart) { + return []; + } + + let base64Data: string; + try { + base64Data = decodeURIComponent(dataPart); + } catch { + // malformed percent-encoding in the migration URI + return []; + } + const wordArrayData = CryptoJS.enc.Base64.parse(base64Data); const byteData = wordArrayToByteArray(wordArrayData); const lines: string[] = []; @@ -94,31 +106,72 @@ export function getOTPAuthPerLineFromOPTAuthMigration(migrationUri: string) { if (byteData[offset] !== 10) { break; } + // Every length byte below is attacker-controlled; validate that each field + // stays inside the buffer before reading, so truncated/garbage payloads + // can't fabricate entries from out-of-bounds (undefined) bytes. + if (offset + 4 > byteData.length) { + break; + } const lineLength = byteData[offset + 1]; const secretStart = offset + 4; const secretLength = byteData[offset + 3]; + const secretEnd = secretStart + secretLength; + if (secretEnd + 2 > byteData.length) { + break; + } + const accountStart = secretEnd + 2; + const accountLength = byteData[secretEnd + 1]; + const accountEnd = accountStart + accountLength; + if (accountEnd + 2 > byteData.length) { + break; + } + const isserStart = accountEnd + 2; + const isserLength = byteData[accountEnd + 1]; + const isserEnd = isserStart + isserLength; + // need bytes up to the type field (isserEnd + 5) + if (isserEnd + 5 >= byteData.length) { + break; + } + const secretBytes = subBytesArray(byteData, secretStart, secretLength); const secret = byteArray2Base32(secretBytes); - const accountStart = secretStart + secretLength + 2; - const accountLength = byteData[secretStart + secretLength + 1]; const accountBytes = subBytesArray(byteData, accountStart, accountLength); const account = byteArray2String(accountBytes); - const isserStart = accountStart + accountLength + 2; - const isserLength = byteData[accountStart + accountLength + 1]; const issuerBytes = subBytesArray(byteData, isserStart, isserLength); const issuer = byteArray2String(issuerBytes); - const algorithm = ["SHA1", "SHA1", "SHA256", "SHA512", "MD5"][ - byteData[isserStart + isserLength + 1] - ]; - const digits = [6, 6, 8][byteData[isserStart + isserLength + 3]]; - const type = ["totp", "hotp", "totp"][ - byteData[isserStart + isserLength + 5] + // index 4 is MD5, which KeyUtilities cannot generate (it would fall back + // to SHA1 and emit wrong codes); map it to undefined so the entry is + // skipped below instead of imported silently. + const algorithm = ["SHA1", "SHA1", "SHA256", "SHA512", undefined][ + byteData[isserEnd + 1] ]; + const digits = [6, 6, 8][byteData[isserEnd + 3]]; + const type = ["totp", "hotp", "totp"][byteData[isserEnd + 5]]; + + // Skip rather than emit otpauth://undefined/...&algorithm=undefined, which + // would silently import an entry that generates wrong codes. + if ( + !secret || + algorithm === undefined || + digits === undefined || + type === undefined + ) { + offset += lineLength + 2; + continue; + } + let line = `otpauth://${type}/${account}?secret=${secret}&issuer=${issuer}&algorithm=${algorithm}&digits=${digits}`; if (type === "hotp") { let counter = 1; - if (isserStart + isserLength + 7 <= lineLength) { - counter = byteData[isserStart + isserLength + 7]; + // counter byte must sit inside this entry ([offset, offset+lineLength+2)) + // and inside the buffer; the old `<= lineLength` compared an absolute + // index against a relative length, so it never read the counter for any + // entry after the first and could read past the buffer on the first. + if ( + isserEnd + 7 < offset + lineLength + 2 && + isserEnd + 7 < byteData.length + ) { + counter = byteData[isserEnd + 7]; } line += `&counter=${counter}`; } diff --git a/src/models/otp.ts b/src/models/otp.ts index cdfa38dba..82784a8e6 100644 --- a/src/models/otp.ts +++ b/src/models/otp.ts @@ -47,10 +47,31 @@ export class OTPUtil { } } +// Upstream encoded an autofill-bound host inside the issuer field as +// "Name::host". Split that into the dedicated host field on read so old +// entries keep working and get normalized on the next save. +function migrateLegacyHost( + issuer: string, + host: string +): { issuer: string; host: string } { + const sepIndex = issuer.lastIndexOf("::"); + if (!host && sepIndex !== -1) { + return { + issuer: issuer.slice(0, sepIndex), + host: issuer + .slice(sepIndex + 2) + .replace(/^\.+/, "") + .toLowerCase(), + }; + } + return { issuer, host }; +} + export class OTPEntry implements OTPEntryInterface { type: OTPType; index: number; issuer: string; + host: string; secret: string | null; account: string; hash: string; @@ -63,7 +84,7 @@ export class OTPEntry implements OTPEntryInterface { encData?: string; encSecret?: string; keyId?: string; - code = "••••••"; + code = "••••••"; constructor( entry: @@ -72,6 +93,7 @@ export class OTPEntry implements OTPEntryInterface { encrypted: boolean; index: number; issuer?: string; + host?: string; secret: string; type: OTPType; counter?: number; @@ -102,6 +124,7 @@ export class OTPEntry implements OTPEntryInterface { // defaults this.type = OTPType.totp; this.issuer = ""; + this.host = ""; this.account = ""; this.counter = 0; this.period = 30; @@ -123,6 +146,11 @@ export class OTPEntry implements OTPEntryInterface { } else { this.issuer = ""; } + { + const migrated = migrateLegacyHost(this.issuer, entry.host || ""); + this.issuer = migrated.issuer; + this.host = migrated.host; + } if (entry.account) { this.account = entry.account; } else { @@ -182,14 +210,14 @@ export class OTPEntry implements OTPEntryInterface { return; } - applyEncryption(encryption: EncryptionInterface) { + async applyEncryption(encryption: EncryptionInterface) { if (!encryption || !encryption.getEncryptionStatus()) { return; } if (this.encSecret) { // v2 encryption - this.secret = encryption.decryptSecretString(this.encSecret); + this.secret = await encryption.decryptSecretString(this.encSecret); if (this.secret) { this.encSecret = ""; } @@ -197,13 +225,14 @@ export class OTPEntry implements OTPEntryInterface { } // check if its a rawotpstorage - const decryptedData = encryption.decryptEncSecret(this); + const decryptedData = await encryption.decryptEncSecret(this); if (decryptedData === null) { return; } if (decryptedData?.dataType !== "OTPStorage") { console.warn("Decrypt successful, but malformed encData!", this.hash); + return; } if (decryptedData.hash !== this.hash) { @@ -212,6 +241,7 @@ export class OTPEntry implements OTPEntryInterface { this.hash, decryptedData.hash ); + return; } this.account = decryptedData.account || ""; @@ -220,6 +250,12 @@ export class OTPEntry implements OTPEntryInterface { this.counter = decryptedData.counter || 0; this.digits = decryptedData.digits || 6; this.issuer = decryptedData.issuer || ""; + this.host = decryptedData.host || ""; + { + const migrated = migrateLegacyHost(this.issuer, this.host); + this.issuer = migrated.issuer; + this.host = migrated.host; + } this.period = decryptedData.period || 30; this.pinned = decryptedData.pinned || false; this.secret = decryptedData.secret; @@ -256,16 +292,17 @@ export class OTPEntry implements OTPEntryInterface { } generate() { - const offset = UserSettings.items ? UserSettings.items.offset : 0; if (!UserSettings.items) { - // browser storage is async, so we need to wait for it to load - // and re-generate the code - // don't change the code to async, it will break the mutation - // for Accounts store to export data + // browser storage is async, so wait for it to load and re-generate. + // don't change this to async: it would break the Accounts store + // mutation that exports data. Return so we don't first generate a + // code with offset 0 and then race the reload over it. UserSettings.updateItems().then(() => { this.generate(); }); + return; } + const offset = UserSettings.items.offset; if (!this.secret && !this.encData) { this.code = CodeState.Invalid; diff --git a/src/models/password.ts b/src/models/password.ts index 2d3bb3102..4ca2fb06e 100644 --- a/src/models/password.ts +++ b/src/models/password.ts @@ -1,55 +1,58 @@ import { BrowserStorage, isOldKey } from "./storage"; -export async function argonHash( - value: string, - salt: string -): Promise { - const iframe = document.getElementById("argon-sandbox"); - const message = { - action: "hash", - value, - salt, - }; - - if (!iframe) { +// Single argon-sandbox round-trip. Every request carries a unique id; the +// listener only accepts the reply that comes from our sandbox iframe and +// matches that id, then removes itself. This replaces the old per-call +// anonymous listeners that were never removed and resolved on whatever message +// arrived first (a correctness hazard when calls overlapped). A timeout turns a +// hung sandbox into a rejection instead of an indefinite wait. +function callArgonSandbox(message: { + action: "hash" | "verify"; + [key: string]: unknown; +}): Promise { + const iframe = document.getElementById( + "argon-sandbox" + ) as HTMLIFrameElement | null; + if (!iframe || !iframe.contentWindow) { throw new Error("argon-sandbox missing!"); } + const sandbox = iframe.contentWindow; + const id = crypto.randomUUID(); - const argonPromise: Promise = new Promise((resolve) => { - window.addEventListener("message", (response) => { - resolve(response.data.response); - }); - // @ts-expect-error bad typings - iframe.contentWindow.postMessage(message, "*"); + return new Promise((resolve, reject) => { + const handler = (event: MessageEvent) => { + if (event.source !== sandbox || !event.data || event.data.id !== id) { + return; + } + window.removeEventListener("message", handler); + clearTimeout(timer); + resolve(event.data.response); + }; + const timer = setTimeout(() => { + window.removeEventListener("message", handler); + reject(new Error("argon2 sandbox timed out")); + }, 60000); + window.addEventListener("message", handler); + // The sandbox iframe has an opaque origin, so "*" is required to post to it; + // the event.source check above is what authenticates the reply. + sandbox.postMessage({ ...message, id }, "*"); }); +} - return argonPromise; +export async function argonHash( + value: string, + salt: string +): Promise { + return (await callArgonSandbox({ action: "hash", value, salt })) as + | string + | undefined; } export async function argonVerify( value: string, hash: string ): Promise { - const iframe = document.getElementById("argon-sandbox"); - const message = { - action: "verify", - value, - hash, - }; - - if (!iframe) { - throw new Error("argon-sandbox missing!"); - } - - const argonPromise: Promise = new Promise((resolve) => { - window.addEventListener("message", (response) => { - resolve(response.data.response); - }); - // @ts-expect-error bad typings - iframe.contentWindow.postMessage(message, "*"); - }); - - return argonPromise; + return (await callArgonSandbox({ action: "verify", value, hash })) as boolean; } // Verify a password using keys in BrowserStorage diff --git a/src/models/settings.ts b/src/models/settings.ts index aac7a84b0..f0cd40eb3 100644 --- a/src/models/settings.ts +++ b/src/models/settings.ts @@ -11,6 +11,7 @@ interface UserSettingsData { driveRevoked?: boolean; driveToken?: string; dropboxEncrypted?: boolean; + dropboxRefreshToken?: string; dropboxRevoked?: boolean; dropboxToken?: string; lastRemindingBackupTime?: number; @@ -28,6 +29,7 @@ interface UserSettingsData { autolock?: number; enableContextMenu?: boolean; encodedPhrase?: string; + onboardingComplete?: boolean; smartFilter?: boolean; theme?: string; zoom?: number; @@ -41,6 +43,7 @@ const LocalUserSettingsDataKeys = [ "driveRevoked", "driveToken", "dropboxEncrypted", + "dropboxRefreshToken", "dropboxRevoked", "dropboxToken", "lastRemindingBackupTime", @@ -184,6 +187,7 @@ type BooleanOption = | "oneDriveBusiness" | "oneDriveEncrypted" | "oneDriveRevoked" + | "onboardingComplete" | "smartFilter"; type NumberOption = "autolock" | "lastRemindingBackupTime" | "offset" | "zoom"; @@ -201,6 +205,7 @@ function isBooleanOption(key: string): key is BooleanOption { "oneDriveBusiness", "oneDriveEncrypted", "oneDriveRevoked", + "onboardingComplete", "smartFilter", ].includes(key); } diff --git a/src/models/storage.ts b/src/models/storage.ts index 4b3699fb2..dd94d6b0b 100644 --- a/src/models/storage.ts +++ b/src/models/storage.ts @@ -63,7 +63,6 @@ export class BrowserStorage { } } - // TODO: promise this static async get() { const storageLocation = await this.getStorageLocation(); const removeOtherData = function (items: Record): void { @@ -129,10 +128,22 @@ export class BrowserStorage { static async set(data: object) { const storageLocation = await this.getStorageLocation(); - if (storageLocation === StorageLocation.Local) { - await chrome.storage.local.set(data); - } else if (storageLocation === StorageLocation.Sync) { - await chrome.storage.sync.set(data); + try { + if (storageLocation === StorageLocation.Local) { + await chrome.storage.local.set(data); + } else if (storageLocation === StorageLocation.Sync) { + await chrome.storage.sync.set(data); + } + } catch (error) { + // Usually the sync quota (MAX_ITEMS / QUOTA_BYTES_PER_ITEM). Re-throw with + // a clear message instead of failing as a silent unhandled rejection that + // leaves the entry visible in the UI but never persisted. + console.error("Storage write failed", storageLocation, error); + throw new Error( + storageLocation === StorageLocation.Sync + ? "Browser sync storage is full. Switch to local storage in Preferences." + : "Storage write failed: " + String(error) + ); } return; } @@ -192,10 +203,11 @@ function isKey(key: unknown): key is Key { } export class EntryStorage { - private static getOTPStorageFromEntry( + private static async getOTPStorageFromEntry( entry: OTPEntry, - unencrypted?: boolean - ): OTPStorage { + unencrypted?: boolean, + forBackup?: boolean + ): Promise { let secret: string; if (!entry.secret && entry.encData && entry.keyId) { return { @@ -238,8 +250,22 @@ export class EntryStorage { storageItem.period = entry.period; } - if (entry.issuer) { - storageItem.issuer = entry.issuer; + if (forBackup && entry.host) { + // Backups encode the bound host as "issuer::host" (the upstream + // convention import's migrateLegacyHost understands) rather than a + // separate host field, so the website round-trips through importers + // that only keep issuer. + storageItem.issuer = entry.issuer + ? `${entry.issuer}::${entry.host}` + : `::${entry.host}`; + } else { + if (entry.issuer) { + storageItem.issuer = entry.issuer; + } + + if (entry.host) { + storageItem.host = entry.host; + } } if (entry.account) { @@ -266,7 +292,7 @@ export class EntryStorage { entry.encryption?.getEncryptionStatus() && entry.encryption.getEncryptionKeyId() ) { - const encData = entry.encryption.getEncryptedString( + const encData = await entry.encryption.getEncryptedString( JSON.stringify(storageItem) ); return { @@ -355,7 +381,7 @@ export class EntryStorage { } } - static getExport(data: OTPEntryInterface[], encrypted?: boolean) { + static async getExport(data: OTPEntryInterface[], encrypted?: boolean) { try { const exportData: { [hash: string]: OTPStorage } = {}; for (const entry of data) { @@ -364,7 +390,11 @@ export class EntryStorage { continue; } - exportData[entry.hash] = this.getOTPStorageFromEntry(entry, !encrypted); + exportData[entry.hash] = await this.getOTPStorageFromEntry( + entry, + !encrypted, + true + ); } return exportData; } catch (error) { @@ -384,11 +414,42 @@ export class EntryStorage { continue; } - const entry = _data[hash]; + let entry = _data[hash]; - // TODO: fix this if (entry.dataType === "EncOTPStorage") { - continue; + if (encrypted) { + // Encrypted backup: keep the ciphertext as-is (plus the keys + // appended below) so decryptBackupData can unlock it on restore. + continue; + } + + // Plaintext export: this entry is still AES-GCM ciphertext and must + // be decrypted here, otherwise it ends up mixed into a supposedly + // "unencrypted" backup - either lost on restore (no passphrase to + // unlock it) or leaked as raw ciphertext mislabeled as plaintext. + const decryptedData = await encryption.decryptEncSecret({ + encData: entry.data, + } as OTPEntryInterface); + + if (!decryptedData) { + // Can't unlock this entry (no/incorrect master password) - drop it + // rather than emit ciphertext disguised as a plaintext entry. + delete _data[hash]; + continue; + } + + const plainEntry: RawOTPStorage = { + ...decryptedData, + encrypted: false, + }; + // Must not carry keyId/dataType forward: FileImport.vue treats any + // entry with a keyId (or encrypted=true) as ciphertext requiring a + // passphrase, which this plaintext entry no longer needs or has. + delete plainEntry.keyId; + delete plainEntry.dataType; + + _data[hash] = plainEntry; + entry = plainEntry; } // remove unnecessary fields @@ -403,7 +464,15 @@ export class EntryStorage { delete entry.period; } - if (!entry.issuer) { + // Encode the bound host as "issuer::host" (upstream convention) so the + // website survives import, and drop the separate host field. Mirrors + // getOTPStorageFromEntry's forBackup branch. + if (entry.host) { + entry.issuer = entry.issuer + ? `${entry.issuer}::${entry.host}` + : `::${entry.host}`; + delete entry.host; + } else if (!entry.issuer) { delete entry.issuer; } @@ -424,7 +493,9 @@ export class EntryStorage { if (!encrypted) { // decrypt the data to export if (entry.encrypted) { - const decryptedSecret = encryption.decryptSecretString(entry.secret); + const decryptedSecret = await encryption.decryptSecretString( + entry.secret + ); if (decryptedSecret !== entry.secret && decryptedSecret !== null) { entry.secret = decryptedSecret; entry.encrypted = false; @@ -463,12 +534,22 @@ export class EntryStorage { continue; } + // type and algorithm are stored as their names (e.g. "hotp", "SHA256"), + // not numbers, so parseInt would yield NaN and silently fall back to the + // default. Map the name back to the enum instead. Fixes imported HOTP / + // Steam entries becoming TOTP (Authenticator-Extension/Authenticator#1292, + // #405) and SHA256/SHA512 reverting to SHA1 (#1442, #1294, #1089). + const typeFromName = OTPType[data[hash].type as keyof typeof OTPType]; const rawAlgorithm = data[hash].algorithm; + const algorithmFromName = rawAlgorithm + ? OTPAlgorithm[rawAlgorithm.toUpperCase() as keyof typeof OTPAlgorithm] + : undefined; const entryData: { account: string; encrypted: false; index: number; issuer: string; + host: string; secret: string; type: OTPType; counter: number; @@ -478,18 +559,20 @@ export class EntryStorage { algorithm: OTPAlgorithm; pinned: boolean; } = { - type: (parseInt(data[hash].type) as OTPType) || OTPType[OTPType.totp], + type: typeof typeFromName === "number" ? typeFromName : OTPType.totp, index: data[hash].index || 0, issuer: data[hash].issuer || "", + host: data[hash].host || "", account: data[hash].account || "", encrypted: false, secret: data[hash].secret, counter: data[hash].counter || 0, period: data[hash].period || 30, digits: data[hash].digits || 6, - algorithm: rawAlgorithm - ? (parseInt(rawAlgorithm) as OTPAlgorithm) - : OTPAlgorithm.SHA1, + algorithm: + typeof algorithmFromName === "number" + ? algorithmFromName + : OTPAlgorithm.SHA1, pinned: data[hash].pinned || false, hash: data[hash].hash || hash, }; @@ -551,7 +634,7 @@ export class EntryStorage { } const entry = new OTPEntry(entryData, encryption); - _data[entryData.hash] = this.getOTPStorageFromEntry(entry); + _data[entryData.hash] = await this.getOTPStorageFromEntry(entry); } _data = this.ensureUniqueIndex(_data); await BrowserStorage.set(_data); @@ -559,7 +642,7 @@ export class EntryStorage { static async add(entry: OTPEntry) { await BrowserStorage.set({ - [entry.hash]: this.getOTPStorageFromEntry(entry), + [entry.hash]: await this.getOTPStorageFromEntry(entry), }); } @@ -568,7 +651,7 @@ export class EntryStorage { if (!Object.prototype.hasOwnProperty.call(_data, entry.hash)) { throw new Error("Entry to change does not exist."); } - const storageItem = this.getOTPStorageFromEntry(entry); + const storageItem = await this.getOTPStorageFromEntry(entry); _data[entry.hash] = storageItem; _data = this.ensureUniqueIndex(_data); await BrowserStorage.set(_data); @@ -576,10 +659,10 @@ export class EntryStorage { static async set(entries: OTPEntry[]) { let _data = await BrowserStorage.get(); - entries.forEach((entry) => { - const storageItem = this.getOTPStorageFromEntry(entry); + for (const entry of entries) { + const storageItem = await this.getOTPStorageFromEntry(entry); _data[entry.hash] = storageItem; - }); + } _data = this.ensureUniqueIndex(_data); await BrowserStorage.set(_data); } @@ -652,6 +735,7 @@ export class EntryStorage { hash: entryData.hash, index: entryData.index, issuer: entryData.issuer, + host: entryData.host, secret: entryData.secret, type, counter: entryData.counter, diff --git a/src/options.ts b/src/options.ts index f9cc047e7..b7fd31701 100644 --- a/src/options.ts +++ b/src/options.ts @@ -1,14 +1,12 @@ -import Vue from "vue"; +import { createApp } from "vue"; import OptionsView from "./components/Options.vue"; import { loadI18nMessages } from "./store/i18n"; async function init() { + const app = createApp(OptionsView); // i18n - Vue.prototype.i18n = await loadI18nMessages(); - - new Vue({ - render: (h) => h(OptionsView), - }).$mount("#options"); + app.config.globalProperties.i18n = await loadI18nMessages(); + app.mount("#options"); } init(); diff --git a/src/permissions.ts b/src/permissions.ts index d0636485c..373bd2451 100644 --- a/src/permissions.ts +++ b/src/permissions.ts @@ -1,6 +1,6 @@ // Vue -import Vue from "vue"; -import Vuex from "vuex"; +import { createApp } from "vue"; +import { createStore } from "vuex"; // Components import PermissionsView from "./components/Permissions.vue"; @@ -11,28 +11,24 @@ import { loadI18nMessages } from "./store/i18n"; import { Permissions } from "./store/Permissions"; async function init() { - // i18n - Vue.prototype.i18n = await loadI18nMessages(); - - // Load modules - Vue.use(Vuex); - - // Load common components globally - for (const component of CommonComponents) { - Vue.component(component.name, component.component); - } - // State - const store = new Vuex.Store({ + const store = createStore({ + strict: process.env.NODE_ENV !== "production", modules: { permissions: await new Permissions().getModule(), }, }); - const instance = new Vue({ - render: (h) => h(PermissionsView), - store, - }).$mount("#permissions"); + const app = createApp(PermissionsView); + app.use(store); + // i18n + app.config.globalProperties.i18n = await loadI18nMessages(); + // Load common components globally + for (const component of CommonComponents) { + app.component(component.name, component.component); + } + + const instance = app.mount("#permissions"); // Set title try { diff --git a/src/popup.ts b/src/popup.ts index 173bc033e..7173ebab0 100644 --- a/src/popup.ts +++ b/src/popup.ts @@ -1,7 +1,6 @@ // Vue -import Vue from "vue"; -import Vuex from "vuex"; -import { Vue2Dragula } from "vue2-dragula"; +import { createApp, h, ComponentPublicInstance } from "vue"; +import { createStore } from "vuex"; // Components import Popup from "./components/Popup.vue"; @@ -17,7 +16,7 @@ import { Menu } from "./store/Menu"; import { Notification } from "./store/Notification"; import { Qr } from "./store/Qr"; import { Advisor } from "./store/Advisor"; -import { Dropbox, Drive, OneDrive } from "./models/backup"; +import { Dropbox, OneDrive } from "./models/backup"; import { syncTimeWithGoogle } from "./syncTime"; import { StorageLocation, UserSettings } from "./models/settings"; @@ -34,20 +33,10 @@ async function init() { await migrateLocalStorageToBrowserStorage(); await UserSettings.updateItems(); - // Add globals - Vue.prototype.i18n = await loadI18nMessages(); - - // Load modules - Vue.use(Vuex); - Vue.use(Vue2Dragula); - - // Load common components globally - for (const component of CommonComponents) { - Vue.component(component.name, component.component); - } - // State - const store = new Vuex.Store({ + const store = createStore({ + // catch out-of-mutation state changes during development (no prod cost) + strict: process.env.NODE_ENV !== "production", modules: { accounts: await new Accounts().getModule(), advisor: await new Advisor().getModule(), @@ -61,9 +50,8 @@ async function init() { }); // Render - const instance = new Vue({ - render: (h) => h(Popup), - store, + const app = createApp({ + render: () => h(Popup), mounted() { // Update time based entries' codes this.$store.commit("accounts/updateCodes"); @@ -71,7 +59,17 @@ async function init() { this.$store.commit("accounts/updateCodes"); }, 1000); }, - }).$mount("#authenticator"); + }); + + app.use(store); + // Add globals + app.config.globalProperties.i18n = await loadI18nMessages(); + // Load common components globally + for (const component of CommonComponents) { + app.component(component.name, component.component); + } + + const instance = app.mount("#authenticator"); // Prompt for password if needed if (instance.$store.state.accounts.shouldShowPassphrase) { @@ -147,7 +145,9 @@ async function init() { if (!searchInput || !searchDiv) { return; } - searchDiv.style.display = "block"; + // force-show before Vue re-renders so focus() lands; must match the + // search box's flex layout (display:block would break it — #1) + searchDiv.style.display = "flex"; searchInput.focus(); } }, @@ -200,7 +200,15 @@ async function init() { init(); -async function runScheduledBackup(clientTime: number, instance: Vue) { +async function runScheduledBackup( + clientTime: number, + instance: ComponentPublicInstance +) { + // A scheduled cloud backup without a master password would upload plaintext + // secrets; skip it entirely. The UI prompts the user to set a password first. + if (!instance.$store.state.accounts.defaultEncryption) { + return; + } if (instance.$store.state.backup.dropboxToken) { chrome.permissions.contains( { origins: ["https://*.dropboxapi.com/*"] }, @@ -228,49 +236,8 @@ async function runScheduledBackup(clientTime: number, instance: Vue) { UserSettings.removeItem("dropboxRevoked"); } } catch (error) { - // ignore - } - } - instance.$store.commit( - "notification/alert", - instance.i18n.remind_backup - ); - UserSettings.items.lastRemindingBackupTime = clientTime; - UserSettings.commitItems(); - } - ); - } - if (instance.$store.state.backup.driveToken) { - chrome.permissions.contains( - { - origins: [ - "https://www.googleapis.com/*", - "https://accounts.google.com/o/oauth2/revoke", - ], - }, - async (hasPermission) => { - if (hasPermission) { - try { - const drive = new Drive(); - const res = await drive.upload( - instance.$store.state.accounts.encryption.get( - instance.$store.state.accounts.defaultEncryption - ) - ); - if (res) { - UserSettings.items.lastRemindingBackupTime = clientTime; - UserSettings.commitItems(); - return; - } else if (UserSettings.items.driveRevoked === true) { - instance.$store.commit( - "notification/alert", - chrome.i18n.getMessage("token_revoked", ["Google Drive"]) - ); - UserSettings.items.driveRevoked = undefined; - UserSettings.removeItem("driveRevoked"); - } - } catch (error) { - // ignore + // a failed scheduled backup shouldn't be completely silent + console.error("Scheduled backup failed", error); } } instance.$store.commit( @@ -312,7 +279,8 @@ async function runScheduledBackup(clientTime: number, instance: Vue) { UserSettings.removeItem("oneDriveRevoked"); } } catch (error) { - // ignore + // a failed scheduled backup shouldn't be completely silent + console.error("Scheduled backup failed", error); } } instance.$store.commit( diff --git a/src/qrdebug.ts b/src/qrdebug.ts index 713c92def..ebceca1ef 100644 --- a/src/qrdebug.ts +++ b/src/qrdebug.ts @@ -12,9 +12,8 @@ chrome.runtime.onMessage.addListener((message, sender) => { message.info.windowWidth ); } - - // https://stackoverflow.com/a/56483156 - return true; + // no response is sent, so don't return true (which leaves the sender's + // message channel open and rejects it on close) }); function getQrDebug( diff --git a/src/store/Accounts.ts b/src/store/Accounts.ts index 57f6a47e6..bfbf1ced1 100644 --- a/src/store/Accounts.ts +++ b/src/store/Accounts.ts @@ -7,8 +7,11 @@ import { getSiteName, getMatchedEntriesHash } from "../utils"; import { isChromium } from "../browser"; import { StorageLocation, UserSettings } from "../models/settings"; import { DataType } from "../models/otp"; +import { argonHash, argonVerify } from "../models/password"; const LegacyEncryption = "LegacyEncryption"; +// uuidv4 shape; entries whose hash is not a uuidv4 get their hash regenerated +const UUIDV4_REGEX = /[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}/i; export class Accounts implements Module { async getModule() { const cachedKeyInfo = await this.getCachedKeyInfo(); @@ -80,17 +83,20 @@ export class Accounts implements Module { stopFilter(state: AccountsState) { state.filter = false; }, + startFilter(state: AccountsState) { + state.filter = true; + }, showSearch(state: AccountsState) { state.showSearch = true; }, updateCodes(state: AccountsState) { let second = new Date().getSeconds(); if (UserSettings.items.offset) { - // prevent second from negative - second += Number(UserSettings.items.offset) + 60; + second += Number(UserSettings.items.offset); } - second = second % 60; + // positive modulo so any offset (incl. < -60) stays in 0..59 (#1310) + second = ((second % 60) + 60) % 60; state.second = second; let currentlyEncrypted = false; @@ -110,49 +116,35 @@ export class Accounts implements Module { state.sectorOffset = -second; } - // if (second > 25) { - // app.class.timeout = true; - // } else { - // app.class.timeout = false; - // } - // if (second < 1) { - // const entries = app.entries as OTP[]; - // for (let i = 0; i < entries.length; i++) { - // if (entries[i].type !== OTPType.hotp && - // entries[i].type !== OTPType.hhex) { - // entries[i].generate(); - // } - // } - // } - const entries = state.entries as OTPEntryInterface[]; - for (let i = 0; i < entries.length; i++) { - if ( - entries[i].type !== OTPType.hotp && - entries[i].type !== OTPType.hhex - ) { - entries[i].generate(); + for (const entry of state.entries) { + if (entry.type !== OTPType.hotp && entry.type !== OTPType.hhex) { + entry.generate(); } } }, loadCodes(state: AccountsState, newCodes: OTPEntryInterface[]) { state.entries = newCodes; }, - moveCode(state: AccountsState, opts: { from: number; to: number }) { - state.entries.splice( - opts.to, - 0, - state.entries.splice(opts.from, 1)[0] - ); - - for (let i = 0; i < state.entries.length; i++) { - if (state.entries[i].index !== i) { - state.entries[i].index = i; - } - } + // vuedraggable hands back the reordered (pinned-first) displayed array; + // re-index by position and store it. The `entries` getter re-derives + // the pinned-first view from this, so drags within a group stick. + reorderEntries(state: AccountsState, reordered: OTPEntryInterface[]) { + reordered.forEach((entry, i) => { + entry.index = i; + }); + state.entries = reordered; }, pinEntry(state: AccountsState, entry: OTPEntryInterface) { state.entries[entry.index].pinned = !entry.pinned; }, + // Re-anchor the timer-circle animation phase to the current second. + // Reordering (pin/drag) re-attaches the entry's DOM node, which restarts + // its CSS animation; without re-syncing it would resume from the stale + // load-time sectorOffset and the countdown circle would drift until the + // popup is reopened. + resyncSector(state: AccountsState) { + state.sectorOffset = -state.second; + }, updateExport( state: AccountsState, exportData: { [k: string]: OTPEntryInterface } @@ -182,18 +174,59 @@ export class Accounts implements Module { initComplete(state: AccountsState) { state.initComplete = true; }, + // --- mutations routed for Vuex strict mode (state must only change here) + removeEntry(state: AccountsState, hash: string) { + const index = state.entries.findIndex((entry) => entry.hash === hash); + if (index > -1) { + state.entries.splice(index, 1); + } + }, + addEntry(state: AccountsState, entry: OTPEntryInterface) { + state.entries.unshift(entry); + }, + setDefaultEncryption(state: AccountsState, keyId: string) { + state.defaultEncryption = keyId; + }, + setEncryption( + state: AccountsState, + payload: { keyId: string; encryption: EncryptionInterface } + ) { + state.encryption.set(payload.keyId, payload.encryption); + }, + setEntryField( + state: AccountsState, + payload: { + entry: OTPEntryInterface; + field: "issuer" | "account" | "host"; + value: string; + } + ) { + payload.entry[payload.field] = payload.value; + }, + applyEntryEncryption( + state: AccountsState, + payload: { entry: OTPEntryInterface; encryption: EncryptionInterface } + ) { + payload.entry.changeEncryption(payload.encryption); + }, + regenEntryHash(state: AccountsState, entry: OTPEntryInterface) { + entry.genUUID(); + }, + // in-memory part of OTPEntry.next(); persistence (entry.update()) stays + // in the action since mutations must be synchronous + advanceHotpCounter(state: AccountsState, entry: OTPEntryInterface) { + entry.generate(); + if (entry.secret !== null) { + entry.counter++; + } + }, }, actions: { deleteCode: async ( state: ActionContext, hash: string ) => { - const index = state.state.entries.findIndex( - (entry) => entry.hash === hash - ); - if (index > -1) { - state.state.entries.splice(index, 1); - } + state.commit("removeEntry", hash); state.commit( "updateExport", await EntryStorage.getExport(state.state.entries) @@ -207,7 +240,7 @@ export class Accounts implements Module { state: ActionContext, entry: OTPEntryInterface ) => { - state.state.entries.unshift(entry); + state.commit("addEntry", entry); state.commit( "updateExport", await EntryStorage.getExport(state.state.entries) @@ -235,23 +268,7 @@ export class Accounts implements Module { // --- handle v2 encryption // decrypt using key const key = CryptoJS.AES.decrypt(encKeys.enc, password).toString(); - const isCorrectPassword = await new Promise( - (resolve: (value: string) => void) => { - const iframe = document.getElementById("argon-sandbox"); - const message = { - action: "verify", - value: key, - hash: encKeys.hash, - }; - if (iframe) { - window.addEventListener("message", (response) => { - resolve(response.data.response); - }); - // @ts-expect-error - bad typings - iframe.contentWindow.postMessage(message, "*"); - } - } - ); + const isCorrectPassword = await argonVerify(key, encKeys.hash); if (!isCorrectPassword) { state.commit("wrongPassword"); @@ -261,19 +278,19 @@ export class Accounts implements Module { return; } - state.state.encryption.set( - LegacyEncryption, - new Encryption(key, LegacyEncryption) - ); + state.commit("setEncryption", { + keyId: LegacyEncryption, + encryption: new Encryption(key, LegacyEncryption), + }); migrationNeeded = true; } else if (encKeys.length === 0) { // --- handle v1 encryption // verify current password - state.state.encryption.set( - LegacyEncryption, - new Encryption(password, LegacyEncryption) - ); + state.commit("setEncryption", { + keyId: LegacyEncryption, + encryption: new Encryption(password, LegacyEncryption), + }); await state.dispatch("updateEntries"); if (state.getters.currentlyEncrypted) { @@ -289,58 +306,29 @@ export class Accounts implements Module { // --- handle v3 encryption // TODO: let user reconcile multiple keys from sync conflicts for (const key of encKeys) { - const rawHash = await new Promise( - (resolve: (value: string) => void) => { - const iframe = document.getElementById("argon-sandbox"); - const message = { - action: "hash", - value: password, - salt: key.salt, - }; - if (iframe) { - window.addEventListener("message", (response) => { - resolve(response.data.response); - }); - // @ts-expect-error bad typings - iframe.contentWindow.postMessage(message, "*"); - } - } - ); + const rawHash = await argonHash(password, key.salt); // https://passlib.readthedocs.io/en/stable/lib/passlib.hash.argon2.html#format-algorithm - const possibleHash = rawHash.split("$")[5]; + const possibleHash = rawHash ? rawHash.split("$")[5] : ""; if (!possibleHash) { throw new Error("argon2 did not return a hash!"); } // verify user password by comparing their password hash with the // hash of their password's hash - const isCorrectPassword = await new Promise( - (resolve: (value: string) => void) => { - const iframe = document.getElementById("argon-sandbox"); - const message = { - action: "verify", - value: possibleHash, - hash: key.hash, - }; - if (iframe) { - window.addEventListener("message", (response) => { - resolve(response.data.response); - }); - // @ts-expect-error bad typings - iframe.contentWindow.postMessage(message, "*"); - } - } + const isCorrectPassword = await argonVerify( + possibleHash, + key.hash ); // TODO: there is a serious bug here. If two keys have the same password, // then only one of them will be used for decryption. if (isCorrectPassword) { - state.state.encryption.set( - key.id, - new Encryption(possibleHash, key.id) - ); - state.state.defaultEncryption = key.id; + state.commit("setEncryption", { + keyId: key.id, + encryption: new Encryption(possibleHash, key.id), + }); + state.commit("setDefaultEncryption", key.id); saltedHash = possibleHash; } @@ -383,8 +371,11 @@ export class Accounts implements Module { version: 3, }; const newEncryption = new Encryption(saltedHash, key.id); - state.state.encryption.set(key.id, newEncryption); - state.state.defaultEncryption = key.id; + state.commit("setEncryption", { + keyId: key.id, + encryption: newEncryption, + }); + state.commit("setDefaultEncryption", key.id); const toRemove: string[] = []; for (const entry of state.state.entries) { @@ -392,15 +383,14 @@ export class Accounts implements Module { continue; } - await entry.changeEncryption(newEncryption); + state.commit("applyEntryEncryption", { + entry, + encryption: newEncryption, + }); // if not uuidv4 regen - if ( - /[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}/i.test( - entry.hash - ) - ) { - entry.genUUID(); + if (UUIDV4_REGEX.test(entry.hash)) { + state.commit("regenEntryHash", entry); toRemove.push(entry.hash); } } @@ -436,7 +426,10 @@ export class Accounts implements Module { entry.encryption?.getEncryptionKeyId() !== state.state.defaultEncryption ) { - await entry.changeEncryption(defaultEncryption); + state.commit("applyEntryEncryption", { + entry, + encryption: defaultEncryption, + }); needUpdateStorage = true; } } @@ -454,7 +447,7 @@ export class Accounts implements Module { }); } - state.commit("style/hideInfo", true, { root: true }); + state.dispatch("style/hideInfo", true, { root: true }); return; }, changePassphrase: async ( @@ -491,24 +484,20 @@ export class Accounts implements Module { version: 3, }; - const linkedKeys = new Map(); + const linkedKeys = new Set(); for (const entry of state.state.entries) { - await entry.changeEncryption(new Encryption(saltedHash, key.id)); + state.commit("applyEntryEncryption", { + entry, + encryption: new Encryption(saltedHash, key.id), + }); // if not uuidv4 regen - if ( - /[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}/i.test( - entry.hash - ) - ) { + if (UUIDV4_REGEX.test(entry.hash)) { removeKeys.push(entry.hash); - entry.genUUID(); + state.commit("regenEntryHash", entry); } if (entry.encryption?.getEncryptionKeyId()) { - linkedKeys.set( - entry.encryption.getEncryptionKeyId(), - undefined - ); + linkedKeys.add(entry.encryption.getEncryptionKeyId()); } } @@ -529,11 +518,11 @@ export class Accounts implements Module { await BrowserStorage.remove(removeKeys); } - state.state.encryption.set( - key.id, - new Encryption(saltedHash, key.id) - ); - state.state.defaultEncryption = key.id; + state.commit("setEncryption", { + keyId: key.id, + encryption: new Encryption(saltedHash, key.id), + }); + state.commit("setDefaultEncryption", key.id); await state.dispatch("updateEntries"); @@ -549,7 +538,10 @@ export class Accounts implements Module { }); } else { for (const entry of state.state.entries) { - await entry.changeEncryption(new Encryption("", "")); + state.commit("applyEntryEncryption", { + entry, + encryption: new Encryption("", ""), + }); } await EntryStorage.set(state.state.entries); @@ -560,7 +552,7 @@ export class Accounts implements Module { if (keyId) { await BrowserStorage.remove(keyId); } - state.state.defaultEncryption = ""; + state.commit("setDefaultEncryption", ""); await state.dispatch("updateEntries"); @@ -595,6 +587,15 @@ export class Accounts implements Module { state.commit("loadCodes", entries); state.commit("updateCodes"); + + // show the search box on load too, not only after clearing a smart + // filter, so it appears for large lists / after unlocking (#1496, #1400) + if ( + state.state.entries.length >= 10 && + !(state.getters.shouldFilter && state.state.filter) + ) { + state.commit("showSearch"); + } state.commit( "updateExport", await EntryStorage.getExport(state.state.entries) @@ -633,9 +634,9 @@ export class Accounts implements Module { ) { UserSettings.items.storageLocation = StorageLocation.Local; await chrome.storage.sync.clear(); - await chrome.storage.local.set({ - UserSettings: UserSettings.items, - }); + // commitItems() strips functions and routes by storageLocation, + // matching the local=>sync branch (was a raw local.set bypass). + await UserSettings.commitItems(); return "updateSuccess"; } else { throw " All data not transferred successfully."; @@ -676,10 +677,10 @@ export class Accounts implements Module { } private async getCachedKeyInfo() { - const { - cachedPassphrase, - cachedKeyId, - } = await chrome.storage.session.get(); + const { cachedPassphrase, cachedKeyId } = await chrome.storage.session.get([ + "cachedPassphrase", + "cachedKeyId", + ]); return { cachedPassphrase, cachedKeyId }; } @@ -694,22 +695,15 @@ async function genHash(value: string) { const randomValues = window.crypto.getRandomValues(new Uint16Array(8)); let salt = ""; for (const byte of randomValues) { - salt += byte.toString(16); + // zero-pad each 16-bit value to 4 hex chars; without padding leading zeros + // were dropped, giving variable-length salts and collisions (e.g. 0x0001 + // and 0x0010 both contributing "1"/"10" ambiguously). + salt += byte.toString(16).padStart(4, "0"); } - return new Promise((resolve: (value: string) => void) => { - const iframe = document.getElementById("argon-sandbox"); - const message = { - action: "hash", - value: value, - salt, - }; - if (iframe) { - window.addEventListener("message", (response) => { - resolve(response.data.response); - }); - // @ts-expect-error bad typings - iframe.contentWindow.postMessage(message, "*"); - } - }); + const hash = await argonHash(value, salt); + if (!hash) { + throw new Error("argon2 did not return a hash!"); + } + return hash; } diff --git a/src/store/Advisor.ts b/src/store/Advisor.ts index a9514d4e8..f82f5f296 100644 --- a/src/store/Advisor.ts +++ b/src/store/Advisor.ts @@ -1,3 +1,4 @@ +import { ActionContext } from "vuex"; import { EntryStorage } from "../models/storage"; import { InsightLevel, AdvisorInsight } from "../models/advisor"; import { StorageLocation, UserSettings } from "../models/settings"; @@ -19,7 +20,9 @@ const insightsData: AdvisorInsightInterface[] = [ validation: async () => { await UserSettings.updateItems(); const hasEncryptedEntry = await EntryStorage.hasEncryptionKey(); - return hasEncryptedEntry && !Number(UserSettings.items.autolock); + // an unset autolock falls back to the 30-min default (see setAutolock), + // so only warn when the user has explicitly disabled it with 0 (#1281) + return hasEncryptedEntry && Number(UserSettings.items.autolock) === 0; }, }, { @@ -58,6 +61,13 @@ const insightsData: AdvisorInsightInterface[] = [ }, ]; +// advisorIgnoreList may be stored as a JSON string (legacy) or an array; +// normalise to an array. +function parseIgnoreList(): string[] { + const raw = UserSettings.items.advisorIgnoreList; + return typeof raw === "string" ? JSON.parse(raw || "[]") : raw || []; +} + export class Advisor implements Module { async getModule() { await UserSettings.updateItems(); @@ -67,26 +77,41 @@ export class Advisor implements Module { ignoreList: UserSettings.items.advisorIgnoreList || [], }, mutations: { - dismissInsight: async (state: AdvisorState, insightId: string) => { + // sync state changes only (these used to be async mutations that + // assigned state after an await, which Vuex strict mode forbids) + pushIgnore(state: AdvisorState, insightId: string) { state.ignoreList.push(insightId); - UserSettings.items.advisorIgnoreList = state.ignoreList; - UserSettings.commitItems(); + }, + setIgnoreList(state: AdvisorState, list: string[]) { + state.ignoreList = list; + }, + setInsights(state: AdvisorState, insights: AdvisorInsightInterface[]) { + state.insights = insights; + }, + }, + actions: { + dismissInsight: async ( + context: ActionContext, + insightId: string + ) => { + context.commit("pushIgnore", insightId); + UserSettings.items.advisorIgnoreList = context.state.ignoreList; + await UserSettings.commitItems(); - state.insights = await this.getInsights(); + context.commit("setInsights", await this.getInsights()); }, - clearIgnoreList: async (state: AdvisorState) => { - state.ignoreList = []; + clearIgnoreList: async ( + context: ActionContext + ) => { + context.commit("setIgnoreList", []); UserSettings.items.advisorIgnoreList = undefined; - UserSettings.commitItems(); + await UserSettings.commitItems(); - state.insights = await this.getInsights(); + context.commit("setInsights", await this.getInsights()); }, - updateInsight: async (state: AdvisorState) => { - state.insights = await this.getInsights(); - state.ignoreList = - typeof UserSettings.items.advisorIgnoreList === "string" - ? JSON.parse(UserSettings.items.advisorIgnoreList || "[]") - : UserSettings.items.advisorIgnoreList || []; + updateInsight: async (context: ActionContext) => { + context.commit("setInsights", await this.getInsights()); + context.commit("setIgnoreList", parseIgnoreList()); }, }, namespaced: true, @@ -95,10 +120,7 @@ export class Advisor implements Module { private async getInsights() { await UserSettings.updateItems(); - const advisorIgnoreList: string[] = - typeof UserSettings.items.advisorIgnoreList === "string" - ? JSON.parse(UserSettings.items.advisorIgnoreList || "[]") - : UserSettings.items.advisorIgnoreList || []; + const advisorIgnoreList = parseIgnoreList(); const filteredInsightsData: AdvisorInsightInterface[] = []; diff --git a/src/store/Backup.ts b/src/store/Backup.ts index 790ed719a..b424b7b78 100644 --- a/src/store/Backup.ts +++ b/src/store/Backup.ts @@ -2,13 +2,15 @@ import { UserSettings } from "../models/settings"; export class Backup implements Module { async getModule() { - UserSettings.updateItems(); + await UserSettings.updateItems(); return { state: { - dropboxEncrypted: UserSettings.items.dropboxEncrypted === true, - driveEncrypted: UserSettings.items.driveEncrypted === true, - oneDriveEncrypted: UserSettings.items.oneDriveEncrypted === true, + // default to encrypted when unset, matching backup.ts upload behaviour + // (an unset preference uploads encrypted, so the UI must reflect that) + dropboxEncrypted: UserSettings.items.dropboxEncrypted !== false, + driveEncrypted: UserSettings.items.driveEncrypted !== false, + oneDriveEncrypted: UserSettings.items.oneDriveEncrypted !== false, dropboxToken: Boolean(UserSettings.items.dropboxToken), driveToken: Boolean(UserSettings.items.driveToken), oneDriveToken: Boolean(UserSettings.items.oneDriveToken), diff --git a/src/store/Menu.ts b/src/store/Menu.ts index 48487d6ed..26e2d27c4 100644 --- a/src/store/Menu.ts +++ b/src/store/Menu.ts @@ -1,7 +1,20 @@ -import { isSafari } from "../browser"; import { UserSettings } from "../models/settings"; import { ManagedStorage } from "../models/storage"; +// Map any stored theme (incl. the retired normal/simple/flat) to a current one. +function normalizeTheme(value?: string): string { + switch (value) { + case "dark": + case "auto": + case "accessibility": + case "compact": + case "light": + return value; + default: + return "light"; + } +} + export class Menu implements Module { async getModule() { await UserSettings.updateItems(); @@ -13,7 +26,8 @@ export class Menu implements Module { useAutofill: UserSettings.items.autofill === true, smartFilter: UserSettings.items.smartFilter === true, enableContextMenu: UserSettings.items.enableContextMenu === true, - theme: UserSettings.items.theme || (isSafari ? "flat" : "normal"), + theme: normalizeTheme(UserSettings.items.theme), + onboardingComplete: UserSettings.items.onboardingComplete === true, autolock: Number(UserSettings.items.autolock) || 30, backupDisabled: await ManagedStorage.get("disableBackup", false), exportDisabled: await ManagedStorage.get("disableExport", false), @@ -53,6 +67,11 @@ export class Menu implements Module { UserSettings.items.theme = theme; UserSettings.commitItems(); }, + setOnboardingComplete(state: MenuState, complete: boolean) { + state.onboardingComplete = complete; + UserSettings.items.onboardingComplete = complete; + UserSettings.commitItems(); + }, setAutolock(state: MenuState, autolock: number) { state.autolock = autolock; UserSettings.items.autolock = autolock; @@ -69,8 +88,8 @@ export class Menu implements Module { private resize(zoom: number) { if (zoom !== 100) { - document.body.style.marginBottom = 480 * (zoom / 100 - 1) + "px"; - document.body.style.marginRight = 320 * (zoom / 100 - 1) + "px"; + document.body.style.marginBottom = 580 * (zoom / 100 - 1) + "px"; + document.body.style.marginRight = 360 * (zoom / 100 - 1) + "px"; document.body.style.transform = "scale(" + zoom / 100 + ")"; } } diff --git a/src/store/Notification.ts b/src/store/Notification.ts index c68d81290..8fce1c5fc 100644 --- a/src/store/Notification.ts +++ b/src/store/Notification.ts @@ -13,12 +13,11 @@ export class Notification implements Module { alert: (state: NotificationState, message: string) => { state.message.unshift(message); }, - closeAlert: (state: NotificationState) => { - state.messageIdle = false; + setMessageIdle: (state: NotificationState, value: boolean) => { + state.messageIdle = value; + }, + shiftMessage: (state: NotificationState) => { state.message.shift(); - setTimeout(() => { - state.messageIdle = true; - }, 200); }, setConfirm: (state: NotificationState, message: string) => { state.confirmMessage = message; @@ -28,13 +27,26 @@ export class Notification implements Module { }, }, actions: { + // was a mutation, but the deferred reset (setTimeout) mutated state + // outside the handler, which Vuex strict mode forbids + closeAlert: ({ commit }: ActionContext) => { + commit("setMessageIdle", false); + commit("shiftMessage"); + setTimeout(() => { + commit("setMessageIdle", true); + }, 200); + }, confirm: async ( state: ActionContext, message: string ) => { return new Promise((resolve: (value: boolean) => void) => { state.commit("setConfirm", message); - window.addEventListener("confirm", (event) => { + // Named handler so it can be removed once it fires; the old + // anonymous listener was added on every dispatch and never removed, + // accumulating and re-firing on later confirms. + const handler = (event: Event) => { + window.removeEventListener("confirm", handler); state.commit("setConfirm", ""); if (!this.isCustomEvent(event)) { resolve(false); @@ -42,7 +54,8 @@ export class Notification implements Module { } resolve(event.detail); return; - }); + }; + window.addEventListener("confirm", handler); }); }, ephermalMessage: ( @@ -50,7 +63,7 @@ export class Notification implements Module { message: string ) => { state.commit("setNotification", message); - state.commit("style/showNotification", null, { root: true }); + state.dispatch("style/showNotification", null, { root: true }); }, }, namespaced: true, diff --git a/src/store/Permissions.ts b/src/store/Permissions.ts index a8d20ebca..a7a73e5c2 100644 --- a/src/store/Permissions.ts +++ b/src/store/Permissions.ts @@ -1,3 +1,4 @@ +import { ActionContext } from "vuex"; import { Permission } from "../models/permission"; import { UserSettings } from "../models/settings"; @@ -61,86 +62,6 @@ const permissions: Permission[] = [ }, ], }, - { - id: "https://www.googleapis.com/*", - description: chrome.i18n.getMessage("permission_drive"), - revocable: true, - validation: [ - async () => { - await UserSettings.updateItems(); - if (UserSettings.items.driveToken !== undefined) { - return { - valid: false, - message: chrome.i18n.getMessage("permission_drive_cannot_revoke"), - }; - } - return { - valid: true, - }; - }, - ], - }, - { - id: "https://accounts.google.com/*", - description: chrome.i18n.getMessage("permission_drive"), - revocable: true, - validation: [ - async () => { - await UserSettings.updateItems(); - if (UserSettings.items.driveToken !== undefined) { - return { - valid: false, - message: chrome.i18n.getMessage("permission_drive_cannot_revoke"), - }; - } - return { - valid: true, - }; - }, - ], - }, - { - id: "https://graph.microsoft.com/*", - description: chrome.i18n.getMessage("permission_onedrive"), - revocable: true, - validation: [ - async () => { - await UserSettings.updateItems(); - if (UserSettings.items.oneDriveToken !== undefined) { - return { - valid: false, - message: chrome.i18n.getMessage( - "permission_onedrive_cannot_revoke" - ), - }; - } - return { - valid: true, - }; - }, - ], - }, - { - id: "https://login.microsoftonline.com/*", - description: chrome.i18n.getMessage("permission_onedrive"), - revocable: true, - validation: [ - async () => { - await UserSettings.updateItems(); - if (UserSettings.items.oneDriveToken !== undefined) { - return { - valid: false, - message: chrome.i18n.getMessage( - "permission_onedrive_cannot_revoke" - ), - }; - } - return { - valid: true, - }; - }, - ], - }, ]; export class Permissions implements Module { @@ -150,8 +71,15 @@ export class Permissions implements Module { permissions: await this.getPermissions(), }, mutations: { + setPermissions(state: PermissionsState, permissions: Permission[]) { + state.permissions = permissions; + }, + }, + actions: { + // was an async mutation; assigning state after an await violates Vuex + // strict mode, so the async work lives in an action now revokePermission: async ( - state: PermissionsState, + context: ActionContext, permissionId: string ) => { const permissionObject = this.getPermissionById(permissionId); @@ -163,17 +91,15 @@ export class Permissions implements Module { ).filter((result) => !result.valid); if (validationResults.length > 0) { - const messages = await Promise.all( - validationResults.map( - async (result) => "• " + (await result).message - ) + const messages = validationResults.map( + (result) => "• " + result.message ); alert(messages.join("\n")); return; } await this.revokePermission(permissionId); - state.permissions = await this.getPermissions(); + context.commit("setPermissions", await this.getPermissions()); }, }, namespaced: true, @@ -251,11 +177,11 @@ export class Permissions implements Module { ); } } + + // nothing matched -> nothing to remove + resolve(); } ); - - // Timeout for remove permissions failed - setTimeout(resolve, 100); }); } } diff --git a/src/store/Qr.ts b/src/store/Qr.ts index 1f77682cb..3251206f8 100644 --- a/src/store/Qr.ts +++ b/src/store/Qr.ts @@ -1,12 +1,21 @@ +interface QrData { + src: string; + issuer: string; + account: string; + monogram: string; + monoBg: string; + monoFg: string; +} + export class Qr implements Module { getModule() { return { state: { - qr: "", + qr: null as QrData | null, }, mutations: { - setQr(state: { qr: string }, url: string) { - state.qr = `url(${url})`; + setQr(state: { qr: QrData | null }, data: QrData) { + state.qr = data; }, }, namespaced: true, diff --git a/src/store/Style.ts b/src/store/Style.ts index a70345691..b4b0bed0c 100644 --- a/src/store/Style.ts +++ b/src/store/Style.ts @@ -1,3 +1,5 @@ +import { ActionContext } from "vuex"; + export class Style implements Module { getModule() { return { @@ -18,17 +20,19 @@ export class Style implements Module { }, }, mutations: { + // generic synchronous flag setter so the animation actions below can + // schedule their deferred resets through a mutation (Vuex strict mode + // forbids the setTimeout callbacks mutating state directly) + setStyleFlag( + state: StyleState, + payload: { key: keyof StyleState["style"]; value: boolean } + ) { + state.style[payload.key] = payload.value; + }, showMenu(state: StyleState) { state.style.slidein = true; state.style.slideout = false; }, - hideMenu(state: StyleState) { - state.style.slidein = false; - state.style.slideout = true; - setTimeout(() => { - state.style.slideout = false; - }, 200); - }, showInfo(state: StyleState, noAnimate?: boolean) { if (noAnimate) { state.style.show = true; @@ -37,45 +41,62 @@ export class Style implements Module { state.style.fadeout = false; } }, - hideInfo(state: StyleState, noAnimate?: boolean) { + showQr(state: StyleState) { + state.style.qrfadein = true; + state.style.qrfadeout = false; + }, + toggleEdit(state: StyleState) { + state.style.isEditing = !state.style.isEditing; + }, + toggleHotpDisabled(state: StyleState) { + state.style.hotpDisabled = !state.style.hotpDisabled; + }, + }, + actions: { + // these end an animation by resetting a flag after a delay, which has + // to be committed (not mutated directly) under strict mode + hideMenu({ commit }: ActionContext) { + commit("setStyleFlag", { key: "slidein", value: false }); + commit("setStyleFlag", { key: "slideout", value: true }); + setTimeout(() => { + commit("setStyleFlag", { key: "slideout", value: false }); + }, 200); + }, + hideInfo( + { commit }: ActionContext, + noAnimate?: boolean + ) { if (noAnimate) { - state.style.show = false; + commit("setStyleFlag", { key: "show", value: false }); } else { - state.style.fadein = false; - state.style.fadeout = true; + commit("setStyleFlag", { key: "fadein", value: false }); + commit("setStyleFlag", { key: "fadeout", value: true }); } setTimeout(() => { - state.style.fadeout = false; + commit("setStyleFlag", { key: "fadeout", value: false }); }, 200); }, - showQr(state: StyleState) { - state.style.qrfadein = true; - state.style.qrfadeout = false; - }, - hideQr(state: StyleState) { - state.style.qrfadein = false; - state.style.qrfadeout = true; + hideQr({ commit }: ActionContext) { + commit("setStyleFlag", { key: "qrfadein", value: false }); + commit("setStyleFlag", { key: "qrfadeout", value: true }); setTimeout(() => { - state.style.qrfadeout = false; + commit("setStyleFlag", { key: "qrfadeout", value: false }); }, 200); }, - showNotification(state: StyleState) { - state.style.notificationFadein = true; - state.style.notificationFadeout = false; + showNotification({ commit }: ActionContext) { + commit("setStyleFlag", { key: "notificationFadein", value: true }); + commit("setStyleFlag", { key: "notificationFadeout", value: false }); setTimeout(() => { - state.style.notificationFadein = false; - state.style.notificationFadeout = true; + commit("setStyleFlag", { key: "notificationFadein", value: false }); + commit("setStyleFlag", { key: "notificationFadeout", value: true }); setTimeout(() => { - state.style.notificationFadeout = false; + commit("setStyleFlag", { + key: "notificationFadeout", + value: false, + }); }, 200); }, 1000); }, - toggleEdit(state: StyleState) { - state.style.isEditing = !state.style.isEditing; - }, - toggleHotpDisabled(state: StyleState) { - state.style.hotpDisabled = !state.style.hotpDisabled; - }, }, getters: { // Returns true if menu or info screen shown diff --git a/src/store/i18n.ts b/src/store/i18n.ts index 983f97c50..f1b127b5f 100644 --- a/src/store/i18n.ts +++ b/src/store/i18n.ts @@ -1,34 +1,13 @@ export async function loadI18nMessages() { - return new Promise( - ( - resolve: (value: { [key: string]: string }) => void, - reject: (reason: Error) => void - ) => { - try { - const xhr = new XMLHttpRequest(); - xhr.overrideMimeType("application/json"); - xhr.onreadystatechange = () => { - if (xhr.readyState === 4) { - const i18nMessage: I18nMessage = JSON.parse(xhr.responseText); - const i18nData: { [key: string]: string } = {}; - for (const key of Object.keys(i18nMessage)) { - i18nData[key] = chrome.i18n.getMessage(key); - } - return resolve(i18nData); - } - return; - }; - xhr.open("GET", chrome.runtime.getURL("/_locales/en/messages.json")); - xhr.send(); - } catch (error) { - if (typeof error === "string" || error === undefined) { - return reject(Error(error)); - } else if (error instanceof Error) { - return reject(error); - } else { - return reject(Error(String(error))); - } - } - } + // bundled extension resource; only the keys are used -- each value is + // resolved through chrome.i18n.getMessage for the active locale. + const response = await fetch( + chrome.runtime.getURL("/_locales/en/messages.json") ); + const i18nMessage: I18nMessage = await response.json(); + const i18nData: { [key: string]: string } = {}; + for (const key of Object.keys(i18nMessage)) { + i18nData[key] = chrome.i18n.getMessage(key); + } + return i18nData; } diff --git a/src/syncTime.ts b/src/syncTime.ts index 7e46074cc..b21578ccd 100644 --- a/src/syncTime.ts +++ b/src/syncTime.ts @@ -21,14 +21,16 @@ export async function syncTimeWithGoogle() { return resolve("updateFailure"); } const serverTime = new Date(date).getTime(); + if (isNaN(serverTime)) { + // unparseable date header — report a failure, not a huge offset + return resolve("updateFailure"); + } const clientTime = new Date().getTime(); const offset = Math.round((serverTime - clientTime) / 1000); if (Math.abs(offset) <= 300) { // within 5 minutes - UserSettings.items.offset = Math.round( - (serverTime - clientTime) / 1000 - ); + UserSettings.items.offset = offset; UserSettings.commitItems(); return resolve("updateSuccess"); } else { diff --git a/src/test/components/Popup/AddAccountPage.test.ts b/src/test/components/Popup/AddAccountPage.test.ts new file mode 100644 index 000000000..ea30f1814 --- /dev/null +++ b/src/test/components/Popup/AddAccountPage.test.ts @@ -0,0 +1,84 @@ +import "mocha"; +import * as chai from "chai"; +import * as sinon from "sinon"; +import * as sinonChai from "sinon-chai"; + +import { mount } from "@vue/test-utils"; +import { toRaw } from "vue"; +import { createStore, Store } from "vuex"; +import CommonComponents from "../../../components/common/index"; + +import AddAccountPage from "../../../components/Popup/AddAccountPage.vue"; +import { EntryStorage } from "../../../models/storage"; +import { OTPType, OTPAlgorithm } from "../../../models/otp"; +import { loadI18nMessages } from "../../../store/i18n"; + +chai.should(); +chai.use(sinonChai); +mocha.setup("bdd"); + +describe("AddAccountPage", () => { + let i18n: { [key: string]: string }; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const components: { [name: string]: any } = {}; + + before(async () => { + i18n = await loadI18nMessages(); + for (const component of CommonComponents) { + components[component.name] = component.component; + } + }); + + // a minimal stand-in for a real encryption instance held in the Map + const fakeEncryption = { getEncryptionStatus: () => true }; + const addCode = sinon.fake(); + + const storeOpts = { + modules: { + accounts: { + state: { + OTPType, + OTPAlgorithm, + encryption: new Map([["key-1", fakeEncryption]]), + defaultEncryption: "key-1", + }, + actions: { addCode }, + namespaced: true, + }, + style: { + actions: { hideInfo: sinon.fake() }, + mutations: { toggleEdit: () => undefined }, + namespaced: true, + }, + notification: { + mutations: { alert: () => undefined }, + namespaced: true, + }, + }, + }; + let store: Store; + + beforeEach(() => { + addCode.resetHistory(); + // don't touch real storage when the entry is created + sinon.stub(EntryStorage, "add").resolves(); + store = createStore(storeOpts); + }); + + it("should construct the new entry with the default encryption instance", async () => { + const wrapper = mount(AddAccountPage, { + global: { plugins: [store], mocks: { i18n }, components }, + }); + + wrapper.vm.newAccount.secret = "aaaaaaaaaaaaaaaa"; // valid base32, >= 16 chars + await wrapper.vm.addNewAccount(); + + addCode.should.have.been.calledOnce; + // regression: encryption Map must be read with .get(), not [] — bracket + // indexing returns undefined and the secret would be stored UNENCRYPTED. + // toRaw: the Map value comes back as a reactive proxy, and chai's `.should` + // getter chokes on Vue's __v_isRef probe, so compare the raw target. + const entry = addCode.lastCall.args[1]; + chai.assert.strictEqual(toRaw(entry.encryption), fakeEncryption); + }); +}); diff --git a/src/test/components/Popup/EnterPasswordPage.test.ts b/src/test/components/Popup/EnterPasswordPage.test.ts index ab1c16489..5374cd5be 100644 --- a/src/test/components/Popup/EnterPasswordPage.test.ts +++ b/src/test/components/Popup/EnterPasswordPage.test.ts @@ -3,8 +3,8 @@ import * as chai from "chai"; import * as sinon from "sinon"; import * as sinonChai from "sinon-chai"; -import { mount, createLocalVue } from "@vue/test-utils"; -import Vuex, { Store } from "vuex"; +import { mount } from "@vue/test-utils"; +import { createStore, Store } from "vuex"; import CommonComponents from "../../../components/common/index"; import EnterPasswordPage from "../../../components/Popup/EnterPasswordPage.vue"; @@ -13,18 +13,20 @@ import { loadI18nMessages } from "../../../store/i18n"; const should = chai.should(); chai.use(sinonChai); mocha.setup("bdd"); -const localVue = createLocalVue(); describe("EnterPasswordPage", () => { + let i18n: { [key: string]: string }; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const components: { [name: string]: any } = {}; + before(async () => { - localVue.prototype.i18n = await loadI18nMessages(); - localVue.use(Vuex); + i18n = await loadI18nMessages(); for (const component of CommonComponents) { - localVue.component(component.name, component.component); + components[component.name] = component.component; } }); - let storeOpts = { + const storeOpts = { modules: { accounts: { actions: { @@ -39,14 +41,20 @@ describe("EnterPasswordPage", () => { }; let store: Store; + const mountPage = (attach = false) => + mount(EnterPasswordPage, { + global: { plugins: [store], mocks: { i18n }, components }, + ...(attach ? { attachTo: document.body } : {}), + }); + beforeEach(() => { // TODO: find a nicer var storeOpts.modules.accounts.actions.applyPassphrase.resetHistory(); - store = new Vuex.Store(storeOpts); + store = createStore(storeOpts); }); it("should apply password when button is clicked", async () => { - const wrapper = mount(EnterPasswordPage, { store, localVue }); + const wrapper = mountPage(); const passwordInput = wrapper.find("input"); const passwordButton = wrapper.find("button"); @@ -60,7 +68,7 @@ describe("EnterPasswordPage", () => { }); it("should apply password when enter is pressed", async () => { - const wrapper = mount(EnterPasswordPage, { store, localVue }); + const wrapper = mountPage(); const passwordInput = wrapper.find("input"); @@ -73,11 +81,7 @@ describe("EnterPasswordPage", () => { }); it("should autofocus password input", () => { - const wrapper = mount(EnterPasswordPage, { - store, - localVue, - attachToDocument: true, - }); + const wrapper = mountPage(true); const passwordInput = wrapper.find("input"); @@ -85,7 +89,9 @@ describe("EnterPasswordPage", () => { }); it("should not show incorrect password message", () => { - const wrapper = mount(EnterPasswordPage, { store, localVue }); + // isVisible() reads getComputedStyle, which only reflects v-show's + // display:none for elements attached to the live document, so attach. + const wrapper = mountPage(true); const errorText = wrapper.find("label.warning"); @@ -98,7 +104,7 @@ describe("EnterPasswordPage", () => { }); it("should show incorrect password message", () => { - const wrapper = mount(EnterPasswordPage, { store, localVue }); + const wrapper = mountPage(); const errorText = wrapper.find("label.warning"); diff --git a/src/test/components/Popup/MenuPage.test.ts b/src/test/components/Popup/MenuPage.test.ts index f8c169b1c..91df8faae 100644 --- a/src/test/components/Popup/MenuPage.test.ts +++ b/src/test/components/Popup/MenuPage.test.ts @@ -2,34 +2,23 @@ import "mocha"; import * as chai from "chai"; import { assert } from "chai"; import * as sinonChai from "sinon-chai"; -import { createLocalVue, mount, Wrapper } from "@vue/test-utils"; -import Vuex, { Store } from "vuex"; +import * as sinon from "sinon"; +import { mount, VueWrapper } from "@vue/test-utils"; +import { createStore, Store } from "vuex"; -import { loadI18nMessages } from "../../../store/i18n"; import MenuPage from "../../../components/Popup/MenuPage.vue"; -import { Style } from "../../../store/Style"; -import { Accounts } from "../../../store/Accounts"; -import { Backup } from "../../../store/Backup"; -import { CurrentView } from "../../../store/CurrentView"; -import { Menu } from "../../../store/Menu"; -import { Notification } from "../../../store/Notification"; -import { Qr } from "../../../store/Qr"; - -import chrome from "sinon-chrome"; - chai.should(); chai.use(sinonChai); mocha.setup("bdd"); -const localVue = createLocalVue(); describe("MenuPage", () => { - before(async () => { - localVue.prototype.i18n = await loadI18nMessages(); - localVue.use(Vuex); - }); + // chrome.i18n.getMessage returns "" in the test extension, so titles bound + // to i18n.* render empty. Use a fixed map; only `feedback` is asserted on + // (the feedback button is found via *[title='Feedback']). + const i18n: { [key: string]: string } = { feedback: "Feedback" }; - let storeOpts = { + const storeOpts = { menu: { state: { version: "1.2.3", @@ -40,133 +29,33 @@ describe("MenuPage", () => { let store: Store<{}>; - let wrapper: Wrapper; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let wrapper: VueWrapper; - before(() => { - // mock the chrome global object - global.chrome.tabs.create = chrome.tabs.create; - global.chrome.storage.managed.get = chrome.storage.managed.get; - }); + const mountMenu = () => + mount(MenuPage, { + global: { plugins: [store], mocks: { i18n } }, + }); beforeEach(async () => { - store = new Vuex.Store({ + store = createStore({ modules: storeOpts, }); - wrapper = mount(MenuPage, { - store, - localVue, - }); + wrapper = mountMenu(); }); - const clickMenuPageButtonByTitle = async ( - wrapper: Wrapper, - title: string - ) => wrapper.find(`*[title='${title}']`).trigger("click"); - describe("feedback button", () => { - // mocks the user agent for testing purposes - const mockUserAgent = (userAgent: string) => { - Object.defineProperty(global, "navigator", { - value: { - userAgent, - }, - configurable: true, - enumerable: true, - writable: true, - }); - }; - - beforeEach(() => { - wrapper = mount(MenuPage, { - store, - localVue, - }); - }); - - it("should open a new tab to the Chrome help page when the feedback button is clicked and the user agent is Chrome", async () => { - mockUserAgent( - "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)" - ); - await clickMenuPageButtonByTitle(wrapper, "Feedback"); - assert.ok( - chrome.tabs.create.withArgs({ url: "https://otp.ee/chromeissues" }) - .calledOnce, - "Tab create should be called with the Chrome URL" - ); - }); - - it("should open a new tab to the Edge help page when the feedback button is clicked and the user agent is Edge", async () => { - mockUserAgent( - "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.74 Safari/537.36 Edg/79.0.309.43" - ); - await clickMenuPageButtonByTitle(wrapper, "Feedback"); - assert.ok( - chrome.tabs.create.withArgs({ url: "https://otp.ee/edgeissues" }) - .calledOnce, - "Tab create should be called with the Edge URL" - ); - }); - - it("should open a new tab to the Firefox help page when the feedback button is clicked and the user agent is Firefox", async () => { - mockUserAgent( - "Mozilla/5.0 (Windows NT x.y; rv:10.0) Gecko/20100101 Firefox/10.0" - ); - await clickMenuPageButtonByTitle(wrapper, "Feedback"); + it("opens the GitHub issues page", async () => { + const openStub = sinon.stub(window, "open"); + await wrapper.find("*[title='Feedback']").trigger("click"); assert.ok( - chrome.tabs.create.withArgs({ url: "https://otp.ee/firefoxissues" }) - .calledOnce, - "Tab create should be called with the Firefox URL" + openStub.calledWith( + "https://github.com/Hank076/Authenticator/issues", + "_blank" + ), + "window.open should be called with the GitHub issues URL" ); - }); - - it("should open a new tab to the Chrome help page when the feedback button is clicked and the user agent is unknown", async () => { - mockUserAgent("Unknown"); - await clickMenuPageButtonByTitle(wrapper, "Feedback"); - assert.ok( - chrome.tabs.create.withArgs({ url: "https://otp.ee/chromeissues" }) - .called, - "Tab create should be called with the Chrome URL" - ); - }); - - describe("feedbackURL is set", () => { - beforeEach(async () => { - try { - chrome.storage.managed.get.yieldsAsync({ - feedbackURL: "https://authenticator.cc", - }); - - store = new Vuex.Store({ - modules: { - backup: await new Backup().getModule(), - currentView: new CurrentView().getModule(), - notification: new Notification().getModule(), - qr: new Qr().getModule(), - style: new Style().getModule(), - menu: await new Menu().getModule(), - accounts: await new Accounts().getModule(), - }, - }); - - wrapper = mount(MenuPage, { - store, - localVue, - }); - } catch (e) { - console.error(e); - // Doesn't show up in mocha? - throw e; - } - }); - - it("should open a new tab to the page specified in ManagedStorage", async () => { - await clickMenuPageButtonByTitle(wrapper, "Feedback"); - assert.ok( - chrome.tabs.create.withArgs({ url: "https://authenticator.cc" }) - .called, - "Tab create should be called with the feedback URL" - ); - }); + openStub.restore(); }); }); diff --git a/src/test/utils.test.ts b/src/test/utils.test.ts new file mode 100644 index 000000000..00db36309 --- /dev/null +++ b/src/test/utils.test.ts @@ -0,0 +1,338 @@ +import "mocha"; +import { expect } from "chai"; +import { getMatchedEntries, cloudBackupAllowed } from "../utils"; +import { EntryStorage } from "../models/storage"; +import { OTPEntry, OTPType } from "../models/otp"; +import { Encryption } from "../models/encryption"; +import { getEntryDataFromOTPAuthPerLine } from "../import"; +import { KeyUtilities } from "../models/key-utilities"; + +// getSiteName() returns [title, nameFromDomain, hostname]. autofill paths call +// getMatchedEntries(siteName, entries, strict=true). These tests pin the strict +// (host-bound) matching contract: a live OTP is only ever offered for a page +// whose real host matches the entry's bound host. +function site( + hostname: string, + nameFromDomain = "", + title = "" +): Array { + return [title, nameFromDomain, hostname]; +} + +function entry(issuer: string, host?: string): OTPEntryInterface { + return ({ issuer, host } as unknown) as OTPEntryInterface; +} + +describe("getMatchedEntries strict (host-bound autofill)", () => { + it("matches when the bound host exactly equals the page host", () => { + const entries = [entry("MyBank", "accounts.google.com")]; + const matched = getMatchedEntries( + site("accounts.google.com", "google"), + entries, + true + ); + expect(matched).to.be.an("array").with.lengthOf(1); + }); + + it("matches a subdomain of the bound host", () => { + const entries = [entry("MyBank", "google.com")]; + const matched = getMatchedEntries( + site("accounts.google.com", "google"), + entries, + true + ); + expect(matched).to.be.an("array").with.lengthOf(1); + }); + + it("does NOT match when the bound host differs, even if the issuer name coincides with the page", () => { + // issuer "Google" used to match the host "google" by name; with host + // binding the mismatching bound host (mybank.com) must win and refuse. + const entries = [entry("Google", "mybank.com")]; + const matched = getMatchedEntries( + site("accounts.google.com", "google"), + entries, + true + ); + expect(matched).to.deep.equal([]); + }); + + it("does NOT match an attacker suffix of the bound host", () => { + const entries = [entry("MyBank", "google.com")]; + const matched = getMatchedEntries( + site("google.com.attacker.com", "attacker"), + entries, + true + ); + expect(matched).to.deep.equal([]); + }); + + it("does NOT autofill an entry that has no bound host", () => { + // No host binding: even though the issuer name matches the page, strict + // autofill must refuse to inject a live code. + const entries = [entry("Google", undefined)]; + const matched = getMatchedEntries( + site("google.com", "google"), + entries, + true + ); + expect(matched).to.deep.equal([]); + }); + + it("migrates a legacy issuer::host binding for strict matching", () => { + // Upstream encoded the bound host in the issuer field as "Name::host". + const entries = [entry("Google::google.com", undefined)]; + const matched = getMatchedEntries( + site("accounts.google.com", "google"), + entries, + true + ); + expect(matched).to.be.an("array").with.lengthOf(1); + }); + + it("splits on the LAST :: when the issuer name itself contains ::", () => { + // "My::Bank" is the issuer, "example.com" is the bound host. A naive + // split("::") would wrongly cut it into issuer "My" + host "bank::example.com". + const entries = [entry("My::Bank::example.com", undefined)]; + const matched = getMatchedEntries( + site("example.com", "example"), + entries, + true + ); + expect(matched).to.be.an("array").with.lengthOf(1); + }); +}); + +describe("getMatchedEntries loose (display filtering, unchanged)", () => { + it("still matches by issuer name against the real host", () => { + const entries = [entry("Google", undefined)]; + const matched = getMatchedEntries( + site("google.com", "google"), + entries, + false + ); + expect(matched).to.be.an("array").with.lengthOf(1); + }); +}); + +// A cloud backup must never carry plaintext secrets off the device. Uploading +// is only permitted once a master password is set, so the export is encrypted +// before it leaves for Dropbox / Drive / OneDrive. cloudBackupAllowed only +// reads getEncryptionStatus(), so a tiny stub stands in for a real Encryption. +describe("cloudBackupAllowed (no plaintext cloud upload without a password)", () => { + const withPassword = ({ + getEncryptionStatus: () => true, + } as unknown) as EncryptionInterface; + const withoutPassword = ({ + getEncryptionStatus: () => false, + } as unknown) as EncryptionInterface; + + it("blocks cloud upload when no master password is set", () => { + expect(cloudBackupAllowed(withoutPassword)).to.equal(false); + }); + + it("allows cloud upload once a master password is set", () => { + expect(cloudBackupAllowed(withPassword)).to.equal(true); + }); + + it("blocks cloud upload when no encryption instance is provided", () => { + expect(cloudBackupAllowed(undefined)).to.equal(false); + }); +}); + +// The bound host must survive a storage round-trip (save -> reload). Without a +// master password an entry is stored as plaintext and rebuilt via the explicit +// field list in EntryStorage.get(), which used to drop the host field. +describe("EntryStorage preserves the bound host across reload", () => { + it("keeps entry.host after add() and get()", async () => { + const entry = new OTPEntry({ + type: OTPType.totp, + index: 0, + issuer: "MyBank", + host: "accounts.example.com", + account: "user", + encrypted: false, + secret: "AAAAAAAAAAAAAAAA", + }); + try { + await EntryStorage.add(entry); + const reloaded = (await EntryStorage.get()).find( + (e) => e.hash === entry.hash + ); + expect(reloaded && reloaded.host).to.equal("accounts.example.com"); + } finally { + await EntryStorage.delete(entry); + } + }); +}); + +// Backups encode the bound host into issuer as "issuer::host" (upstream +// convention) instead of a separate host field, so the website survives a +// round-trip through import's migrateLegacyHost. +describe("EntryStorage.getExport encodes the bound host into issuer", () => { + it("emits issuer::host with no separate host field, and it round-trips", async () => { + const entry = new OTPEntry({ + type: OTPType.totp, + index: 0, + issuer: "MyBank", + host: "accounts.example.com", + account: "user", + encrypted: false, + secret: "AAAAAAAAAAAAAAAA", + }); + const exported = (await EntryStorage.getExport([entry], false)) as { + [hash: string]: RawOTPStorage; + }; + const item = exported[entry.hash]; + expect(item.issuer).to.equal("MyBank::accounts.example.com"); + expect(item.host).to.equal(undefined); + + // import side restores the dedicated host field + const restored = new OTPEntry({ + type: OTPType.totp, + index: 0, + issuer: item.issuer, + host: item.host, + account: "user", + encrypted: false, + secret: "AAAAAAAAAAAAAAAA", + }); + expect(restored.issuer).to.equal("MyBank"); + expect(restored.host).to.equal("accounts.example.com"); + }); + + it("round-trips an issuer that itself contains :: by splitting on the LAST ::", async () => { + // A naive split("::") on "My::Bank::example.com" would wrongly produce + // issuer "My" + host "bank::example.com" and corrupt the issuer name. + const entry = new OTPEntry({ + type: OTPType.totp, + index: 0, + issuer: "My::Bank", + host: "example.com", + account: "user", + encrypted: false, + secret: "AAAAAAAAAAAAAAAA", + }); + const exported = (await EntryStorage.getExport([entry], false)) as { + [hash: string]: RawOTPStorage; + }; + const item = exported[entry.hash]; + expect(item.issuer).to.equal("My::Bank::example.com"); + + const restored = new OTPEntry({ + type: OTPType.totp, + index: 0, + issuer: item.issuer, + host: item.host, + account: "user", + encrypted: false, + secret: "AAAAAAAAAAAAAAAA", + }); + expect(restored.issuer).to.equal("My::Bank"); + expect(restored.host).to.equal("example.com"); + }); +}); + +// The otpauth:// URI export carries the bound host on the issuer parameter as +// "issuer::host"; import must parse it back and migrateLegacyHost splits it +// into the dedicated host field. +describe("getEntryDataFromOTPAuthPerLine parses issuer::host", () => { + it("restores the bound host from the issuer parameter", async () => { + const uri = + "otpauth://totp/Mozilla:user%2Bfirefox%40gmail.com" + + "?secret=AAAAAAAAAAAAAAAA&issuer=Mozilla::accounts.example.com"; + const { exportData } = await getEntryDataFromOTPAuthPerLine(uri); + const item = Object.values(exportData)[0]; + expect(item.issuer).to.equal("Mozilla::accounts.example.com"); + + const entry = new OTPEntry({ + type: OTPType.totp, + index: 0, + issuer: item.issuer, + account: item.account, + encrypted: false, + secret: item.secret, + }); + expect(entry.issuer).to.equal("Mozilla"); + expect(entry.host).to.equal("accounts.example.com"); + }); +}); + +// backupGetExport used to unconditionally `continue` on EncOTPStorage entries +// (encrypted accounts), even when asked for a *plaintext* export. That left +// AES-GCM ciphertext sitting inside an "unencrypted" backup: the importer has +// no passphrase to unlock it, so the account was silently lost on restore. +describe("EntryStorage.backupGetExport decrypts EncOTPStorage entries for a plaintext export", () => { + it("round-trips an encrypted account through a plaintext backup export and back through import", async () => { + const encryption = new Encryption( + "p0-round-trip-test-password-hash", + "p0-round-trip-test-key-id" + ); + const originalSecret = "AAAAAAAAAAAAAAAA"; + + const entry = new OTPEntry( + { + type: OTPType.totp, + index: 0, + issuer: "P0TestBank", + account: "user", + encrypted: false, + secret: originalSecret, + }, + encryption + ); + + try { + // With a password-bearing encryption instance, this is persisted as an + // EncOTPStorage (AES-GCM ciphertext) record, not a plain RawOTPStorage. + await EntryStorage.add(entry); + + const exported = (await EntryStorage.backupGetExport( + encryption, + false // encrypted=false -> plaintext export + )) as { [hash: string]: RawOTPStorage }; + + const item = exported[entry.hash]; + expect(item, "exported entry should be present").to.exist; + expect(item.secret).to.equal(originalSecret); + expect(item.encrypted).to.equal(false); + // Must not carry dataType/keyId forward, or FileImport.vue will treat + // this plaintext entry as ciphertext requiring a passphrase. + expect((item as { dataType?: string }).dataType).to.equal(undefined); + expect(item.keyId).to.equal(undefined); + + // Restore side: import must accept it as a plain entry, no passphrase. + const importEncryption = new Encryption("", ""); + await EntryStorage.import(importEncryption, { [entry.hash]: item }); + + const restored = (await EntryStorage.get()).find( + (e) => e.hash === entry.hash + ); + expect(restored, "restored entry should be present").to.exist; + expect(restored && restored.secret).to.equal(originalSecret); + expect(restored && restored.issuer).to.equal("P0TestBank"); + + if (restored) { + await EntryStorage.delete(restored); + } + } finally { + await EntryStorage.delete(entry); + } + }); +}); + +// base32tohex used to map any character outside [A-Z2-7=] to indexOf's -1 +// sentinel and silently fold that into the key bits, producing a +// wrong-but-well-formed OTP with no indication anything was wrong. +describe("KeyUtilities.generate rejects invalid Base32 secrets", () => { + it("throws for a character outside the Base32 alphabet", () => { + expect(() => + KeyUtilities.generate(OTPType.totp, "AAAAAAA1", 0, 30) + ).to.throw("Invalid Base32 string"); + }); + + it("still accepts a valid Base32 secret", () => { + expect(() => + KeyUtilities.generate(OTPType.totp, "AAAAAAAAAAAAAAAA", 0, 30) + ).to.not.throw(); + }); +}); diff --git a/src/utils.ts b/src/utils.ts index 508f06503..ab7675519 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -34,32 +34,32 @@ export async function getSiteName() { // ip address if (/^\d+\.\d+\.\d+\.\d+$/.test(hostname)) { nameFromDomain = hostname; - } - - // local network - if (hostname.indexOf(".") === -1) { - nameFromDomain = hostname; - } + } else { + // local network + if (hostname.indexOf(".") === -1) { + nameFromDomain = hostname; + } - const hostLevelUnits = hostname.split("."); + const hostLevelUnits = hostname.split("."); - if (hostLevelUnits.length === 2) { - nameFromDomain = hostLevelUnits[0]; - } + if (hostLevelUnits.length === 2) { + nameFromDomain = hostLevelUnits[0]; + } - // www.example.com - // example.com.cn - if (hostLevelUnits.length > 2) { + // www.example.com // example.com.cn - if ( - ["com", "net", "org", "edu", "gov", "co"].indexOf( - hostLevelUnits[hostLevelUnits.length - 2] - ) !== -1 - ) { - nameFromDomain = hostLevelUnits[hostLevelUnits.length - 3]; - } else { - // www.example.com - nameFromDomain = hostLevelUnits[hostLevelUnits.length - 2]; + if (hostLevelUnits.length > 2) { + // example.com.cn + if ( + ["com", "net", "org", "edu", "gov", "co"].indexOf( + hostLevelUnits[hostLevelUnits.length - 2] + ) !== -1 + ) { + nameFromDomain = hostLevelUnits[hostLevelUnits.length - 3]; + } else { + // www.example.com + nameFromDomain = hostLevelUnits[hostLevelUnits.length - 2]; + } } } @@ -68,9 +68,14 @@ export async function getSiteName() { return [title, nameFromDomain, hostname]; } +// `strict` is used by autofill, which pastes a live OTP into the page: it drops +// the page-controlled match and anchors the host match to a real domain +// boundary, so a hostile page can't claim another origin's code. Display +// filtering passes strict=false and stays loose. export function getMatchedEntries( siteName: Array<string | null>, - entries: OTPEntryInterface[] + entries: OTPEntryInterface[], + strict = false ) { if (siteName.length < 2) { return false; @@ -79,7 +84,7 @@ export function getMatchedEntries( const matched = []; for (const entry of entries) { - if (isMatchedEntry(siteName, entry)) { + if (isMatchedEntry(siteName, entry, strict)) { matched.push(entry); } } @@ -99,36 +104,61 @@ export function getMatchedEntriesHash( return false; } +// True when `host` is exactly `bound` or a subdomain of it, so that +// "google.com.attacker.com" does NOT match the bound host "google.com". +function hostMatchesDomain(host: string, bound: string) { + host = host.toLowerCase(); + bound = bound.toLowerCase().replace(/^\.+/, ""); + return host === bound || host.endsWith("." + bound); +} + function isMatchedEntry( siteName: Array<string | null>, - entry: OTPEntryInterface + entry: OTPEntryInterface, + strict = false ) { - if (!entry.issuer) { - return false; + const siteTitle = siteName[0] || ""; + const siteNameFromHost = siteName[1] || ""; + const siteHost = siteName[2] || ""; + + // The bound host lives in entry.host; fall back to the legacy "issuer::host" + // encoding for entries not yet migrated (e.g. raw storage objects). The + // issuer itself may contain "::", so only the last "::" is the separator. + const rawIssuer = entry.issuer || ""; + const sepIndex = rawIssuer.lastIndexOf("::"); + const issuerName = sepIndex === -1 ? rawIssuer : rawIssuer.slice(0, sepIndex); + let boundHost = entry.host || ""; + if (!boundHost && sepIndex !== -1) { + boundHost = rawIssuer.slice(sepIndex + 2); + } + boundHost = boundHost.replace(/^\.+/, "").toLowerCase(); + + // strict (autofill): only ever inject a live code when the page's real host + // matches an explicitly bound host. No bound host => never autofill, so a + // hostile page can't harvest a code the user didn't mean for it. + if (strict) { + if (!boundHost) { + return false; + } + return Boolean(siteHost && hostMatchesDomain(siteHost, boundHost)); } - const issuerHostMatches = entry.issuer.split("::"); - const issuer = issuerHostMatches[0].replace(/[^0-9a-z]/gi, "").toLowerCase(); + // loose (display filtering): bound host match, else issuer-name heuristics. + if (boundHost && siteHost && hostMatchesDomain(siteHost, boundHost)) { + return true; + } + const issuer = issuerName.replace(/[^0-9a-z]/gi, "").toLowerCase(); if (!issuer) { return false; } - const siteTitle = siteName[0] || ""; - const siteNameFromHost = siteName[1] || ""; - const siteHost = siteName[2] || ""; - - if (issuerHostMatches.length > 1) { - if (siteHost && siteHost.indexOf(issuerHostMatches[1]) !== -1) { - return true; - } - } - // site title should be more detailed - // so we use siteTitle.indexOf(issuer) + // The page-controlled <title> is only a weak hint, kept for display filtering. if (siteTitle && siteTitle.indexOf(issuer) !== -1) { return true; } + // siteNameFromHost is derived from the real hostname, not page-controlled. if (siteNameFromHost && issuer.indexOf(siteNameFromHost) !== -1) { return true; } @@ -136,6 +166,38 @@ function isMatchedEntry( return false; } +// Strip a legacy "issuer::host" binding down to the display issuer name. The +// issuer text itself may legitimately contain "::", so only the last "::" is +// treated as the host separator (matches migrateLegacyHost in models/otp.ts). +export function stripBoundHost(issuer: string): string { + const sepIndex = issuer.lastIndexOf("::"); + return sepIndex === -1 ? issuer : issuer.slice(0, sepIndex); +} + +// Normalize a user- or page-provided host into a bare lowercase hostname so it +// can be compared with hostMatchesDomain. Accepts a full URL or a bare host. +export function normalizeHost(input: string): string { + const trimmed = input.trim().toLowerCase(); + if (!trimmed) { + return ""; + } + try { + return new URL(trimmed.includes("://") ? trimmed : "https://" + trimmed) + .hostname; + } catch { + return trimmed.replace(/^\.+/, "").replace(/\/.*$/, ""); + } +} + +// A cloud backup must never carry plaintext secrets off the device. Uploading +// is only allowed once a master password is set, so the export is encrypted +// before it leaves for Dropbox / Drive / OneDrive. Lives here (a leaf module) +// rather than in backup.ts so tests can exercise it without dragging in the +// storage <-> otp import cycle. +export function cloudBackupAllowed(encryption?: EncryptionInterface): boolean { + return Boolean(encryption && encryption.getEncryptionStatus()); +} + export async function getCurrentTab() { const currentWindow = await chrome.windows.getCurrent(); const queryOptions = { active: true, windowId: currentWindow.id }; diff --git a/tsconfig.json b/tsconfig.json index 3adffa296..d6120b32c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -26,6 +26,6 @@ "node_modules" ], "vueCompilerOptions": { - "target": 2 + "target": 3 } } diff --git a/view/argon.html b/view/argon.html index d53d7c167..66ea8ec68 100644 --- a/view/argon.html +++ b/view/argon.html @@ -6,7 +6,7 @@ </head> <body> - <script src="../dist/argon.js"></script> + <script src="../js/argon.js"></script> </body> </html> diff --git a/view/import.html b/view/import.html index da1ffe1fb..2c6ef9200 100644 --- a/view/import.html +++ b/view/import.html @@ -9,7 +9,7 @@ <body> <div id="import"></div> - <script src="../dist/import.js"></script> + <script src="../js/import.js"></script> <iframe src="argon.html" id="argon-sandbox" style="display: none;"></iframe> diff --git a/view/options.html b/view/options.html index 20c33c749..60607a344 100644 --- a/view/options.html +++ b/view/options.html @@ -3,11 +3,12 @@ <head> <meta charset="utf-8" /> + <link rel="stylesheet" href="../css/options.css" /> </head> <body> <div id="options"></div> - <script src="../dist/options.js"></script> + <script src="../js/options.js"></script> </body> </html> diff --git a/view/permissions.html b/view/permissions.html index 3c16b4778..75f317bc9 100644 --- a/view/permissions.html +++ b/view/permissions.html @@ -9,7 +9,7 @@ <body> <div id="permissions"></div> - <script src="../dist/permissions.js"></script> + <script src="../js/permissions.js"></script> </body> </html> diff --git a/view/popup.html b/view/popup.html index eae94ccd5..1d0ba04a6 100644 --- a/view/popup.html +++ b/view/popup.html @@ -13,7 +13,7 @@ <body> <div id="authenticator"> </div> - <script src="../dist/popup.js"></script> + <script src="../js/popup.js"></script> <iframe src="argon.html" id="argon-sandbox" style="display: none;"></iframe> diff --git a/view/qrdebug.html b/view/qrdebug.html index 5bef57dcb..13738d6b4 100644 --- a/view/qrdebug.html +++ b/view/qrdebug.html @@ -9,6 +9,6 @@ <h2>QR Scan Debugging Page</h2> <div id="info"></div> <br> <img id="qr" src=""> - <script src="../dist/qrdebug.js"></script> + <script src="../js/qrdebug.js"></script> </head> </html> diff --git a/view/test.html b/view/test.html index 5325f5bd3..9ee42b7e5 100644 --- a/view/test.html +++ b/view/test.html @@ -9,6 +9,6 @@ <body> <div id="mocha"></div> - <script src="../dist/test.js"></script> + <script src="../js/test.js"></script> </body> </html> diff --git a/webpack.config.js b/webpack.config.js index 3eb5c2828..b5e11b3ba 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,4 +1,5 @@ const path = require("path"); +const webpack = require("webpack"); const { VueLoaderPlugin } = require("vue-loader"); const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin"); @@ -39,7 +40,7 @@ module.exports = { }, { test: /\.svg$/, - loader: 'vue-svg-loader' + use: ['vue-loader', 'vue-svg-loader'] }, { test: /\.(png|jpe?g|gif)$/, @@ -54,13 +55,15 @@ module.exports = { }, plugins: [ new VueLoaderPlugin(), - new ForkTsCheckerWebpackPlugin({ - typescript: { - extensions: { - vue: true - } - } - }) + // Vue 3 esm-bundler feature flags (better tree-shaking, silences the warning) + new webpack.DefinePlugin({ + __VUE_OPTIONS_API__: "true", + __VUE_PROD_DEVTOOLS__: "false", + __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: "false" + }), + // .vue type checking is done by vue-tsc (npm run typecheck), not here; + // fork-ts-checker's vue extension needs Vue 2's vue-template-compiler. + new ForkTsCheckerWebpackPlugin() ], resolve: { extensions: [ @@ -81,7 +84,7 @@ module.exports = { } }, output: { - path: path.resolve(__dirname, "dist"), - publicPath: "/dist/" + path: path.resolve(__dirname, "js"), + publicPath: "/js/" } }; diff --git a/webpack.prod.js b/webpack.prod.js index e7bfebabc..28791bbbf 100644 --- a/webpack.prod.js +++ b/webpack.prod.js @@ -3,4 +3,7 @@ const common = require('./webpack.config.js'); module.exports = merge(common, { mode: 'production', + // The base config enables full source maps for development; don't ship them + // (and the .map files build.sh copies) in production release artifacts. + devtool: false, }); diff --git a/webpack.watch.js b/webpack.watch.js index 69f7d3aaa..a8323b273 100644 --- a/webpack.watch.js +++ b/webpack.watch.js @@ -32,7 +32,7 @@ module.exports = merge(dev, { ignored: /node_modules/ }, output: { - path: path.resolve(__dirname, 'test/chrome/dist'), - publicPath: '/test/chrome/dist/' + path: path.resolve(__dirname, 'test/chrome/js'), + publicPath: '/test/chrome/js/' } });