forked from akai/readest
Compare commits
220 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f2640359a0 | |||
| 079aeaedb0 | |||
| bfacadb964 | |||
| 2e98ed44ee | |||
| 952d2a3553 | |||
| 225aa0ca59 | |||
| 37f718b84b | |||
| db8f53a6e1 | |||
| ce345ad67f | |||
| 8e53e625a4 | |||
| 3350bdbdd3 | |||
| 634d789654 | |||
| 0dfa8e96f5 | |||
| 3698e6ca28 | |||
| 595608bd62 | |||
| b8bb1ee71d | |||
| 4298213ce4 | |||
| 5671460618 | |||
| 452dc9f3e6 | |||
| 09e65211b4 | |||
| f46be89036 | |||
| f2f744a2fe | |||
| ceb1963b88 | |||
| 23d74b567a | |||
| 5014932a8f | |||
| 6a5694d2b2 | |||
| df074082a8 | |||
| 847d514441 | |||
| b2a71da2b0 | |||
| 3e5e4d2946 | |||
| e3c05e7648 | |||
| df9ca2d2fa | |||
| 1631ece2bd | |||
| 2ae25f3bbe | |||
| 511093a6ef | |||
| 1ef126f820 | |||
| 1788b86ced | |||
| 3111e93054 | |||
| 288abc678c | |||
| 7e559c5b90 | |||
| eaaeb844f3 | |||
| 097fde203d | |||
| efd85e67d9 | |||
| bd4bf459e3 | |||
| 8fb1745dbb | |||
| 47c6d6d058 | |||
| 564693d456 | |||
| aa3cd2a0c5 | |||
| c581f8ab47 | |||
| 63ed763499 | |||
| 213fc0d21e | |||
| 9bc1494582 | |||
| 33b2ba1609 | |||
| b9add62ba7 | |||
| 23fa2dc8b2 | |||
| 67ac09a290 | |||
| f10c14ae96 | |||
| 438d3ae0c2 | |||
| 842062fe44 | |||
| d01897d62a | |||
| 6eeb8e7580 | |||
| 9cebadba57 | |||
| e5ec9abca3 | |||
| 5e7f09d5c8 | |||
| ffa193d116 | |||
| 686f8988bc | |||
| 8d5c98004b | |||
| 214411919f | |||
| 42c2825e90 | |||
| 8974a87168 | |||
| f3e9983742 | |||
| beaf034035 | |||
| a5f18ab024 | |||
| d1f5934947 | |||
| f03d6f6861 | |||
| 20523b7eaf | |||
| f31c9b2a98 | |||
| 93071e727f | |||
| 2d12210606 | |||
| 88ccc528d4 | |||
| 9d3fc07865 | |||
| 6d465a40fb | |||
| 01c7e4c2e2 | |||
| 4005858242 | |||
| c24f951375 | |||
| dbe1efd1e9 | |||
| 0299cce343 | |||
| ca9f504329 | |||
| df3527ceca | |||
| 73d0838291 | |||
| 8e3574509e | |||
| 9bdf375304 | |||
| effa922597 | |||
| fe484257a0 | |||
| cc0cd9a114 | |||
| 916d63806e | |||
| 59d0118067 | |||
| 957c6b9f32 | |||
| 9c5a1147fa | |||
| 60ad6706ff | |||
| d9199d1182 | |||
| 219edb9faf | |||
| 30a0227ef3 | |||
| 29b5a7cb88 | |||
| abb727870e | |||
| af84a8b110 | |||
| 0dea5d3094 | |||
| 527b95521e | |||
| 396b23ddb4 | |||
| ee0d1549bd | |||
| 6da6b85530 | |||
| cde055dcb7 | |||
| 08957ce3d7 | |||
| 33515f1f09 | |||
| e3c411f043 | |||
| eda71725da | |||
| f19f45c00e | |||
| 6fb7897092 | |||
| 2de941d7fb | |||
| 3e4b7ebb13 | |||
| 2b52f82b57 | |||
| 9fb7e80757 | |||
| 38869ce438 | |||
| 1dbf93c1cf | |||
| aafcab8232 | |||
| bcfe1b8830 | |||
| 5db1847f7e | |||
| 165ca4a6e9 | |||
| 9556e1305f | |||
| f5b686abd9 | |||
| 74e1b733a9 | |||
| a9e0090275 | |||
| 68d57da9ca | |||
| 3cef529ebd | |||
| 85ac399e2c | |||
| e8d09773a2 | |||
| 23905407ca | |||
| 2ab1304b6d | |||
| d94236e127 | |||
| 630025c123 | |||
| 2b73c5a85a | |||
| a03f2c585f | |||
| 8a0216dcf8 | |||
| 59013be586 | |||
| c6a638dbfd | |||
| e54440ad80 | |||
| 446b014f7d | |||
| 9190433ea1 | |||
| d78d135600 | |||
| 04b6c89e58 | |||
| 8eefba2bee | |||
| a254139200 | |||
| 0514bed5b7 | |||
| 49978c5e3f | |||
| b959bf070c | |||
| 49e6877810 | |||
| bb759597e9 | |||
| bf0bb80009 | |||
| 5d61a9808f | |||
| bc36da6b25 | |||
| 280cb75ca4 | |||
| d5d0a9abd3 | |||
| 9f343992b2 | |||
| 7ab7058dba | |||
| c3113ade13 | |||
| 7737c05c8f | |||
| 4fde9955b2 | |||
| 3875c8f239 | |||
| 7bd1b03652 | |||
| 73878c14e3 | |||
| bc2c3a2c9a | |||
| 460d37c656 | |||
| fff9e3221e | |||
| f77fa17cbc | |||
| d4565f7d60 | |||
| e8b1976f4f | |||
| 6b4bb64885 | |||
| fc0c42f7db | |||
| 41e5f27d4d | |||
| a042b4c739 | |||
| a1c1a9f983 | |||
| 423ae77160 | |||
| 05c61a4b1a | |||
| ce8f06b7fb | |||
| f815844577 | |||
| 7fe4d38c51 | |||
| 43edcb4e9e | |||
| e259b29afa | |||
| cd5f57a004 | |||
| 8c0cdb0e22 | |||
| c0df9f1cb6 | |||
| cb1ddaeedb | |||
| 4447dd7b9a | |||
| dcb7602837 | |||
| 60e48742fe | |||
| dd43569778 | |||
| 8433dd6950 | |||
| 379802f580 | |||
| 2dcb5d7c1e | |||
| 98870d9f3e | |||
| 31aeb2289c | |||
| 0f66ecbba1 | |||
| f0d3c9dd4e | |||
| 92aae340d9 | |||
| 3cf40039ee | |||
| 7ed4937d9d | |||
| b3593c0365 | |||
| 6235202066 | |||
| b3d1085ddf | |||
| b2a4ddae60 | |||
| 42f50af27b | |||
| 07f74d8858 | |||
| 9477789db1 | |||
| 8a7d0e1b0d | |||
| 261ce95ed1 | |||
| c5a3b44cbf | |||
| ed68d25b15 | |||
| f7f8872a13 | |||
| be9802a936 | |||
| 84328dcfb2 |
@@ -0,0 +1,15 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: ['readest']
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
||||
polar: # Replace with a single Polar username
|
||||
buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
|
||||
thanks_dev: # Replace with a single thanks.dev username
|
||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||
@@ -11,7 +11,7 @@ jobs:
|
||||
env:
|
||||
RUSTFLAGS: '-C target-cpu=skylake'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: 'true'
|
||||
- name: Install minimal stable with clippy and rustfmt
|
||||
@@ -32,15 +32,20 @@ jobs:
|
||||
|
||||
build_web_app:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
config:
|
||||
- platform: 'web'
|
||||
- platform: 'tauri'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: 'true'
|
||||
|
||||
- name: setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.15.1
|
||||
version: 10.14.0
|
||||
|
||||
- name: setup node
|
||||
uses: actions/setup-node@v4
|
||||
@@ -48,12 +53,31 @@ jobs:
|
||||
node-version: 22
|
||||
cache: pnpm
|
||||
|
||||
- name: cache Next.js build
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: apps/readest-app/.next/cache
|
||||
key: nextjs-${{ runner.os }}-${{ hashFiles('apps/readest-app/package.json', 'pnpm-lock.yaml') }}
|
||||
restore-keys: nextjs-${{ runner.os }}-
|
||||
|
||||
- name: install Dependencies
|
||||
working-directory: apps/readest-app
|
||||
run: |
|
||||
pnpm install && pnpm setup-pdfjs
|
||||
|
||||
- name: build the web App
|
||||
- name: run tests
|
||||
working-directory: apps/readest-app
|
||||
run: |
|
||||
pnpm build-web
|
||||
pnpm test -- --watch=false
|
||||
|
||||
- name: build the web App
|
||||
if: matrix.config.platform == 'web'
|
||||
working-directory: apps/readest-app
|
||||
run: |
|
||||
pnpm build-web && pnpm check:all
|
||||
|
||||
- name: build the Tauri App
|
||||
if: matrix.config.platform == 'tauri'
|
||||
working-directory: apps/readest-app
|
||||
run: |
|
||||
pnpm build && pnpm check:all
|
||||
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
release_version: ${{ steps.get-release-notes.outputs.release_version }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- name: setup node
|
||||
uses: actions/setup-node@v4
|
||||
- name: get version
|
||||
@@ -91,7 +91,7 @@ jobs:
|
||||
runs-on: ${{ matrix.config.os }}
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: initialize git submodules
|
||||
run: git submodule update --init --recursive
|
||||
@@ -99,7 +99,7 @@ jobs:
|
||||
- name: setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.14.4
|
||||
version: 10.14.0
|
||||
|
||||
- name: setup node
|
||||
uses: actions/setup-node@v4
|
||||
@@ -177,6 +177,11 @@ jobs:
|
||||
pnpm tauri icon ../../data/icons/readest-book.png
|
||||
git checkout .
|
||||
|
||||
MANIFEST="src-tauri/gen/android/app/src/main/AndroidManifest.xml"
|
||||
PERMISSION_LINE='<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>'
|
||||
grep -q 'REQUEST_INSTALL_PACKAGES' "$MANIFEST" || \
|
||||
sed -i "/android.permission.INTERNET/a \ $PERMISSION_LINE" "$MANIFEST"
|
||||
|
||||
pushd src-tauri/gen/android
|
||||
echo "keyAlias=${{ secrets.ANDROID_KEY_ALIAS }}" > keystore.properties
|
||||
echo "password=${{ secrets.ANDROID_KEY_PASSWORD }}" >> keystore.properties
|
||||
@@ -315,3 +320,10 @@ jobs:
|
||||
draft: false,
|
||||
prerelease: false
|
||||
})
|
||||
|
||||
upload-to-r2:
|
||||
needs: [get-release, update-release]
|
||||
uses: ./.github/workflows/upload-to-r2.yml
|
||||
with:
|
||||
tag: ${{ needs.get-release.outputs.release_tag }}
|
||||
secrets: inherit
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
name: Retry workflow
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
run_id:
|
||||
required: true
|
||||
jobs:
|
||||
rerun:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: rerun ${{ inputs.run_id }}
|
||||
env:
|
||||
GH_REPO: ${{ github.repository }}
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
gh run watch ${{ inputs.run_id }} > /dev/null 2>&1
|
||||
gh run rerun ${{ inputs.run_id }} --failed
|
||||
@@ -0,0 +1,77 @@
|
||||
name: Upload Release Assets to R2
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
tag:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: 'Release tag name (e.g., v1.2.3)'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
upload-to-r2:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 3
|
||||
strategy:
|
||||
fail-fast: false
|
||||
env:
|
||||
RELEASE_R2_BUCKET: readest-releases
|
||||
RELEASE_R2_ACCOUNT_ID: ${{ secrets.RELEASE_R2_ACCOUNT_ID }}
|
||||
RELEASE_R2_ACCESS_KEY_ID: ${{ secrets.RELEASE_R2_ACCESS_KEY_ID }}
|
||||
RELEASE_R2_SECRET_ACCESS_KEY: ${{ secrets.RELEASE_R2_SECRET_ACCESS_KEY }}
|
||||
|
||||
steps:
|
||||
- name: Download release assets
|
||||
run: |
|
||||
gh release download "${{ inputs.tag }}" --repo readest/readest --dir ./release-assets
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Install rclone
|
||||
run: curl https://rclone.org/install.sh | sudo bash
|
||||
|
||||
- name: Configure rclone
|
||||
run: |
|
||||
mkdir -p ~/.config/rclone
|
||||
cat > ~/.config/rclone/rclone.conf <<EOF
|
||||
[r2]
|
||||
type = s3
|
||||
provider = Cloudflare
|
||||
access_key_id = $RELEASE_R2_ACCESS_KEY_ID
|
||||
secret_access_key = $RELEASE_R2_SECRET_ACCESS_KEY
|
||||
endpoint = https://${RELEASE_R2_ACCOUNT_ID}.r2.cloudflarestorage.com
|
||||
EOF
|
||||
|
||||
- name: Modify latest.json download URLs
|
||||
run: |
|
||||
GITHUB_BASE_URL="https://github.com/readest/readest/releases/download"
|
||||
READEST_BASE_URL="https://download.readest.com/releases"
|
||||
sed -i "s#${GITHUB_BASE_URL}#${READEST_BASE_URL}#g" ./release-assets/latest.json
|
||||
|
||||
- name: Upload to R2
|
||||
run: |
|
||||
mkdir releases
|
||||
mv ./release-assets/latest.json releases
|
||||
mv ./release-assets/release-notes.json releases
|
||||
rclone copy ./release-assets r2:${RELEASE_R2_BUCKET}/releases/${{ inputs.tag }}/
|
||||
rclone copy ./releases r2:${RELEASE_R2_BUCKET}/releases/
|
||||
|
||||
- name: Upload successful
|
||||
if: success()
|
||||
run: echo "Upload completed successfully"
|
||||
|
||||
retry-on-failure:
|
||||
if: failure() && fromJSON(github.run_attempt) < 3
|
||||
needs: [upload-to-r2]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- env:
|
||||
GH_REPO: ${{ github.repository }}
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: gh workflow run retry-workflow.yml -F run_id=${{ github.run_id }}
|
||||
@@ -11,7 +11,7 @@ jobs:
|
||||
build_and_deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: 'true'
|
||||
- uses: amondnet/vercel-action@v25
|
||||
|
||||
@@ -38,3 +38,5 @@ next-env.d.ts
|
||||
|
||||
# Rust build
|
||||
target
|
||||
|
||||
fastlane/report.xml
|
||||
|
||||
@@ -4,3 +4,6 @@
|
||||
[submodule "packages/tauri"]
|
||||
path = packages/tauri
|
||||
url = https://github.com/readest/tauri.git
|
||||
[submodule "packages/tauri-plugins"]
|
||||
path = packages/tauri-plugins
|
||||
url = https://github.com/readest/tauri-plugins-workspace.git
|
||||
|
||||
@@ -5,5 +5,6 @@
|
||||
"tabWidth": 2,
|
||||
"singleQuote": true,
|
||||
"jsxSingleQuote": true,
|
||||
"endOfLine": "lf",
|
||||
"plugins": ["prettier-plugin-tailwindcss"]
|
||||
}
|
||||
|
||||
+2
-2
@@ -3,14 +3,14 @@
|
||||
When contributing to `Readest`, whether on GitHub or in other community spaces:
|
||||
|
||||
- Be respectful, civil, and open-minded.
|
||||
- Before opening a new pull request, try searching through the [issue tracker](https://github.com/chrox/readest/issues) for known issues or fixes.
|
||||
- Before opening a new pull request, try searching through the [issue tracker](https://github.com/readest/readest/issues) for known issues or fixes.
|
||||
- If you want to make code changes based on your personal opinion(s), make sure you open an issue first describing the changes you want to make, and open a pull request only when your suggestions get approved by maintainers.
|
||||
|
||||
## How to Contribute
|
||||
|
||||
### Prerequisites
|
||||
|
||||
In order to not waste your time implementing a change that has already been declined, or is generally not needed, start by [opening an issue](https://github.com/chrox/readest/issues/new/choose) describing the problem you would like to solve.
|
||||
In order to not waste your time implementing a change that has already been declined, or is generally not needed, start by [opening an issue](https://github.com/readest/readest/issues/new/choose) describing the problem you would like to solve.
|
||||
|
||||
For the best experience to build Readest for yourself, use a recent version of Node.js and Rust. Refer to the [Tauri documentation](https://v2.tauri.app/start/prerequisites/) for details on setting up the development environment prerequisites on different platforms.
|
||||
|
||||
|
||||
Generated
+905
-801
File diff suppressed because it is too large
Load Diff
+26
-1
@@ -2,9 +2,29 @@
|
||||
members = [
|
||||
"apps/readest-app/src-tauri",
|
||||
"packages/tauri/crates/tauri",
|
||||
"packages/tauri/crates/tauri-utils",
|
||||
"packages/tauri/crates/tauri-build",
|
||||
"packages/tauri-plugins/plugins/fs",
|
||||
"packages/tauri-plugins/plugins/dialog",
|
||||
"packages/tauri-plugins/plugins/deep-link",
|
||||
]
|
||||
resolver = "2"
|
||||
|
||||
[workspace.dependencies]
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
tracing = "0.1"
|
||||
log = "0.4"
|
||||
tauri = { version = "2", default-features = false }
|
||||
tauri-build = "2"
|
||||
tauri-plugin = "2"
|
||||
tauri-utils = "2"
|
||||
schemars = "0.8"
|
||||
serde_json = "1"
|
||||
thiserror = "2"
|
||||
glob = "0.3"
|
||||
dunce = "1"
|
||||
url = "2"
|
||||
|
||||
[workspace.package]
|
||||
authors = ["Bilingify LLC"]
|
||||
homepage = "https://readest.com"
|
||||
@@ -15,4 +35,9 @@ edition = "2021"
|
||||
rust-version = "1.77.2"
|
||||
|
||||
[patch.crates-io]
|
||||
tauri = { path = "packages/tauri/crates/tauri" }
|
||||
tauri = { path = "packages/tauri/crates/tauri" }
|
||||
tauri-utils = { path = "packages/tauri/crates/tauri-utils" }
|
||||
tauri-build = { path = "packages/tauri/crates/tauri-build" }
|
||||
tauri-plugin-fs = { path = "packages/tauri-plugins/plugins/fs" }
|
||||
tauri-plugin-dialog = { path = "packages/tauri-plugins/plugins/dialog" }
|
||||
tauri-plugin-deep-link = { path = "packages/tauri-plugins/plugins/deep-link" }
|
||||
|
||||
@@ -15,8 +15,11 @@
|
||||
[![][badge-discord]][link-discord]
|
||||
[![AGPL Licence][badge-license]](LICENSE)
|
||||
[![Latest release][badge-release]][link-gh-releases]
|
||||
[![Donate][badge-donate]][link-donate]
|
||||
<br>
|
||||
[![Last commit][badge-last-commit]][link-gh-commits]
|
||||
[![Commits][badge-commit-activity]][link-gh-pulse]
|
||||
[![Ask DeepWiki][badge-deepwiki]][link-deepwiki]
|
||||
|
||||
</div>
|
||||
|
||||
@@ -27,6 +30,7 @@
|
||||
<a href="#downloads">Downloads</a> •
|
||||
<a href="#getting-started">Getting Started</a> •
|
||||
<a href="#troubleshooting">Troubleshooting</a> •
|
||||
<a href="#support">Support</a> •
|
||||
<a href="#license">License</a>
|
||||
</p>
|
||||
|
||||
@@ -53,6 +57,7 @@
|
||||
| **File Association and Open With** | Quickly open files in Readest in your file browser with one-click. | ✅ |
|
||||
| **Sync across Platforms** | Synchronize book files, reading progress, notes, and bookmarks across all supported platforms. | ✅ |
|
||||
| **Translate with DeepL** | From a single sentence to the entire book—translate instantly with DeepL. | ✅ |
|
||||
| **Translate with Yandex** | Instantly translate text or books using Yandex Translate. | ✅ |
|
||||
| **Text-to-Speech (TTS) Support** | Enjoy smooth, multilingual narration—even within a single book. | ✅ |
|
||||
| **Library Management** | Organize, sort, and manage your entire ebook library. | ✅ |
|
||||
| **Code Syntax Highlighting** | Read software manuals with rich coloring of code examples. | ✅ |
|
||||
@@ -64,8 +69,8 @@
|
||||
|
||||
| **Feature** | **Description** | **Priority** |
|
||||
| ------------------------------- | ------------------------------------------------------------------------------------------ | ------------ |
|
||||
| **Sync with Koreader** | Synchronize reading progress, notes, and bookmarks with [Koreader][link-koreader] devices. | 🛠 |
|
||||
| **AI-Powered Summarization** | Generate summaries of books or chapters using AI for quick insights. | 🛠 |
|
||||
| **Sync with Koreader** | Synchronize reading progress, notes, and bookmarks with [Koreader][link-koreader] devices. | 🔄 |
|
||||
| **Keyboard Navigation** | Implement vimium-style keybindings for book navigation. | 🔄 |
|
||||
| **Support OPDS/Calibre** | Integrate OPDS/Calibre to access online libraries and catalogs. | 🔄 |
|
||||
| **Audiobook Support** | Extend functionality to play and manage audiobooks. | 🔄 |
|
||||
@@ -87,13 +92,22 @@ Stay tuned for continuous improvements and updates! Contributions and suggestion
|
||||
|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
---
|
||||
|
||||
## Downloads
|
||||
|
||||
The Readest app is available for download! 🥳 🚀
|
||||
### Mobile Apps
|
||||
|
||||
<div align="center">
|
||||
<a href="https://apps.apple.com/app/id6738622779">
|
||||
<img alt="Download on the App Store" src="https://developer.apple.com/assets/elements/badges/download-on-the-app-store.svg" style="height: 50px;" /></a>
|
||||
<a href="https://play.google.com/store/apps/details?id=com.bilingify.readest">
|
||||
<img alt="Get it on Google Play" src="https://upload.wikimedia.org/wikipedia/commons/7/78/Google_Play_Store_badge_EN.svg" style="height: 50px;" /></a>
|
||||
</div>
|
||||
|
||||
### Platform-Specific Downloads
|
||||
|
||||
- macOS / iOS / iPadOS : Search for "Readest" on the [App Store][link-appstore], also available on TestFlight for beta test (send your Apple ID to <readestapp@gmail.com> to request access).
|
||||
- Windows / Linux / Android: Visit [https://readest.com][link-website] or the [Releases on GitHub][link-gh-releases].
|
||||
@@ -122,13 +136,13 @@ To get started with Readest, follow these steps to clone and build the project.
|
||||
```bash
|
||||
git clone https://github.com/readest/readest.git
|
||||
cd readest
|
||||
git submodule update --init --recursive
|
||||
```
|
||||
|
||||
### 2. Install Dependencies
|
||||
|
||||
```bash
|
||||
# might need to rerun this when code is updated
|
||||
git submodule update --init --recursive
|
||||
pnpm install
|
||||
# copy pdfjs-dist to Next.js public directory
|
||||
pnpm --filter @readest/readest-app setup-pdfjs
|
||||
@@ -153,6 +167,8 @@ For Windows targets, “Build Tools for Visual Studio 2022” (or a higher editi
|
||||
pnpm tauri dev
|
||||
# or start development for the Web app
|
||||
pnpm dev-web
|
||||
# preview with OpenNext build for the Web app
|
||||
pnpm preview
|
||||
```
|
||||
|
||||
For Android:
|
||||
@@ -243,6 +259,20 @@ Readest is open-source, and contributions are welcome! Feel free to open issues,
|
||||
</p>
|
||||
</a>
|
||||
|
||||
## Support
|
||||
|
||||
If Readest has been useful to you, consider supporting its development. Your contribution helps us squash bugs faster, improve performance, and keep building great features.
|
||||
|
||||
### How to Donate
|
||||
|
||||
1. **GitHub Sponsors**
|
||||
Back the project directly on GitHub:
|
||||
👉 [https://github.com/sponsors/readest](https://github.com/sponsors/readest)
|
||||
|
||||
2. **Crypto Donations**
|
||||
Prefer crypto? You can donate here:
|
||||
👉 [https://donate.readest.com/](https://donate.readest.com/)
|
||||
|
||||
## License
|
||||
|
||||
Readest is free software: you can redistribute it and/or modify it under the terms of the [GNU Affero General Public License](https://www.gnu.org/licenses/agpl-3.0.html) as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. See the [LICENSE](LICENSE) file for details.
|
||||
@@ -277,6 +307,9 @@ The following fonts are utilized in this software, either bundled within the app
|
||||
[badge-commit-activity]: https://img.shields.io/github/commit-activity/m/readest/readest
|
||||
[badge-discord]: https://img.shields.io/discord/1314226120886976544?color=5865F2&label=discord&labelColor=black&logo=discord&logoColor=white&style=flat-square
|
||||
[badge-hellogithub]: https://abroad.hellogithub.com/v1/widgets/recommend.svg?rid=8a5b6ade2aee461a8bd94e59200682a7&claim_uid=eRLUbPOy2qZtDgw&theme=small
|
||||
[badge-donate]: https://donate.readest.com/badge.svg
|
||||
[badge-deepwiki]: https://deepwiki.com/badge.svg
|
||||
[link-donate]: https://donate.readest.com/?tickers=btc%2Ceth%2Csol%2Cusdc
|
||||
[link-appstore]: https://apps.apple.com/app/apple-store/id6738622779?pt=127463130&ct=github&mt=8
|
||||
[link-website]: https://readest.com?utm_source=github&utm_medium=referral&utm_campaign=readme
|
||||
[link-web-readest]: https://web.readest.com
|
||||
@@ -288,3 +321,4 @@ The following fonts are utilized in this software, either bundled within the app
|
||||
[link-parallel-read]: https://readest.com/#parallel-read
|
||||
[link-koreader]: https://github.com/koreader/koreader
|
||||
[link-hellogithub]: https://hellogithub.com/repository/8a5b6ade2aee461a8bd94e59200682a7
|
||||
[link-deepwiki]: https://deepwiki.com/readest/readest
|
||||
|
||||
@@ -2,5 +2,11 @@ PDFJS_BUILD_PATH=../../packages/foliate-js/node_modules/pdfjs-dist/legacy/build
|
||||
PDFJS_FONTS_PATH=../../packages/foliate-js/node_modules/pdfjs-dist
|
||||
PDFJS_STYLE_PATH=../../packages/foliate-js/vendor/pdfjs
|
||||
|
||||
NEXT_PUBLIC_DEV_SUPABASE_URL=https://gxkhxxxeapexynpouyjz.supabase.co
|
||||
NEXT_PUBLIC_DEV_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Imd4a2h4eHhlYXBleHlucG91eWp6Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3MzQ0MzAwNTksImV4cCI6MjA1MDAwNjA1OX0.jhinkQsimQoidsg_U59YD5ROw4PmMJQNKuyXbr4TiQA
|
||||
NEXT_PUBLIC_DEFAULT_POSTHOG_URL_BASE64="aHR0cHM6Ly91cy5pLnBvc3Rob2cuY29t"
|
||||
NEXT_PUBLIC_DEFAULT_POSTHOG_KEY_BASE64="cGhjX2ViNXowbVRxWm8yZm5YYnZGNmE3bFh5TThpTmRSNTNsR1A3VFM3VGh4S08="
|
||||
|
||||
NEXT_PUBLIC_DEFAULT_SUPABASE_URL_BASE64="aHR0cHM6Ly9yZWFkZXN0LnN1cGFiYXNlLmNv"
|
||||
NEXT_PUBLIC_DEFAULT_SUPABASE_KEY_BASE64="ZXlKaGJHY2lPaUpJVXpJMU5pSXNJblI1Y0NJNklrcFhWQ0o5LmV5SnBjM01pT2lKemRYQmhZbUZ6WlNJc0luSmxaaUk2SW5aaWMzbDRablZ6YW1weFpIaHJhbkZzZVhOaklpd2ljbTlzWlNJNkltRnViMjRpTENKcFlYUWlPakUzTXpReE1qTTJOekVzSW1WNGNDSTZNakEwT1RZNU9UWTNNWDAuM1U1VXFhb3VfMVNnclZlMWVvOXJBcGMwdUtqcWhwUWRVWGh2d1VIbVVmZw=="
|
||||
|
||||
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY_DEV_BASE64="cGtfdGVzdF81MVJmQmdLRTdSWW5pTWsxc0tDV2RUd2hMZzcySzk4eDRWcjlIdDdsRFBONngzcnpZYmhydGtNQnpDdzZKbHFaRVVITVp5eVNjVXhCZXVkcGppWTk0WXNHcDAweFlRRnRRaUU="
|
||||
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY_BASE64="cGtfbGl2ZV81MVFYN3dRRU5ndjJFOUxQRHpZUlE5TlJJeTNjd09EZ1AzSkNFRHRPWlFtdFJWc3Brd053ZE1NNUpIVnVPTmJWcjZ3VGFCMUNZR1pJMmRPVWppTkY0bHJvVjAwalE4TkpkdWk="
|
||||
|
||||
@@ -6,6 +6,8 @@ NEXT_PUBLIC_SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY
|
||||
|
||||
NEXT_PUBLIC_STORAGE_FIXED_QUOTA=1073741824
|
||||
|
||||
NEXT_PUBLIC_API_BASE_URL=https://your-api-base-url.com
|
||||
|
||||
SUPABASE_ADMIN_KEY=YOUR_SUPABASE_ADMIN_KEY
|
||||
|
||||
DEEPL_PRO_API_KEYS=YOUR_DEEPL_PRO_API_KEYS
|
||||
|
||||
@@ -26,6 +26,8 @@ module.exports = {
|
||||
'id',
|
||||
'vi',
|
||||
'ar',
|
||||
'th',
|
||||
'bo',
|
||||
'zh-CN',
|
||||
'zh-TW',
|
||||
],
|
||||
|
||||
@@ -1,18 +1,22 @@
|
||||
import withPWAInit from '@ducanh2912/next-pwa';
|
||||
import { initOpenNextCloudflareForDev } from '@opennextjs/cloudflare';
|
||||
import withBundleAnalyzer from '@next/bundle-analyzer';
|
||||
|
||||
const isDev = process.env['NODE_ENV'] === 'development';
|
||||
const appPlatform = process.env['NEXT_PUBLIC_APP_PLATFORM'];
|
||||
|
||||
if (isDev) {
|
||||
const { initOpenNextCloudflareForDev } = await import('@opennextjs/cloudflare');
|
||||
initOpenNextCloudflareForDev();
|
||||
}
|
||||
|
||||
const exportOutput = appPlatform !== 'web' && !isDev;
|
||||
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
// Ensure Next.js uses SSG instead of SSR
|
||||
// https://nextjs.org/docs/pages/building-your-application/deploying/static-exports
|
||||
output: appPlatform === 'web' || isDev ? undefined : 'export',
|
||||
output: exportOutput ? 'export' : undefined,
|
||||
pageExtensions: exportOutput ? ['jsx', 'tsx'] : ['js', 'jsx', 'ts', 'tsx'],
|
||||
// Note: This feature is required to use the Next.js Image component in SSG mode.
|
||||
// See https://nextjs.org/docs/messages/export-image-api for different workarounds.
|
||||
images: {
|
||||
@@ -22,6 +26,19 @@ const nextConfig = {
|
||||
// Configure assetPrefix or else the server won't properly resolve your assets.
|
||||
assetPrefix: '',
|
||||
reactStrictMode: true,
|
||||
serverExternalPackages: ['isows'],
|
||||
transpilePackages: !isDev
|
||||
? [
|
||||
'i18next-browser-languagedetector',
|
||||
'react-i18next',
|
||||
'i18next',
|
||||
'@ducanh2912/next-pwa',
|
||||
'@tauri-apps',
|
||||
'highlight.js',
|
||||
'foliate-js',
|
||||
'marked',
|
||||
]
|
||||
: [],
|
||||
async headers() {
|
||||
return [
|
||||
{
|
||||
@@ -52,4 +69,8 @@ const withPWA = withPWAInit({
|
||||
},
|
||||
});
|
||||
|
||||
export default withPWA(nextConfig);
|
||||
const withAnalyzer = withBundleAnalyzer({
|
||||
enabled: process.env.ANALYZE === 'true',
|
||||
});
|
||||
|
||||
export default withPWA(withAnalyzer(nextConfig));
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@readest/readest-app",
|
||||
"version": "0.9.56",
|
||||
"version": "0.9.72",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "dotenv -e .env.tauri -- next dev",
|
||||
@@ -11,6 +11,7 @@
|
||||
"start-web": "dotenv -e .env.web -- next start",
|
||||
"i18n:extract": "i18next-scanner",
|
||||
"lint": "next lint",
|
||||
"test": "dotenv -e .env -e .env.test.local vitest",
|
||||
"tauri": "tauri",
|
||||
"prepare-public-vendor": "mkdirp ./public/vendor/pdfjs",
|
||||
"copy-pdfjs-js": "cpx \"../../packages/foliate-js/node_modules/pdfjs-dist/legacy/build/{pdf.worker.min.mjs,pdf.mjs,pdf.d.mts}\" ./public/vendor/pdfjs",
|
||||
@@ -30,48 +31,62 @@
|
||||
"build-ios-appstore": "dotenv -e .env.ios-appstore.local -- tauri ios build --export-method app-store-connect",
|
||||
"release-macos-universial-appstore": "dotenv -e .env.tauri.local -e .env.apple-appstore.local -- bash scripts/release-mac-appstore.sh",
|
||||
"release-ios-appstore": "dotenv -e .env.ios-appstore.local -- bash scripts/release-ios-appstore.sh",
|
||||
"release-google-play": "dotenv -e .env.google-play.local -- bash scripts/release-google-play.sh",
|
||||
"config-wrangler": "sed -i \"s/\\${TRANSLATIONS_KV_ID}/$TRANSLATIONS_KV_ID/g\" wrangler.toml",
|
||||
"preview": "NEXT_PUBLIC_APP_PLATFORM=web opennextjs-cloudflare build && opennextjs-cloudflare preview",
|
||||
"preview": "NEXT_PUBLIC_APP_PLATFORM=web opennextjs-cloudflare build && opennextjs-cloudflare preview --ip 0.0.0.0",
|
||||
"deploy": "NEXT_PUBLIC_APP_PLATFORM=web opennextjs-cloudflare build && opennextjs-cloudflare deploy",
|
||||
"upload": "NEXT_PUBLIC_APP_PLATFORM=web opennextjs-cloudflare build && opennextjs-cloudflare upload",
|
||||
"cf-typegen": "wrangler types --env-interface CloudflareEnv cloudflare-env.d.ts"
|
||||
"cf-typegen": "wrangler types --env-interface CloudflareEnv cloudflare-env.d.ts",
|
||||
"check:optional-chaining": "count=$(grep -rno '\\?\\.[a-zA-Z_$]' .next/static/chunks/* out/_next/static/chunks/* | wc -l); if [ \"$count\" -gt 0 ]; then echo '❌ Optional chaining found in output!'; exit 1; else echo '✅ No optional chaining found.'; fi",
|
||||
"check:translations": "count=$(grep -rno '__STRING_NOT_TRANSLATED__' public/locales/* | wc -l); if [ \"$count\" -gt 0 ]; then echo '❌ Untranslated strings found!'; exit 1; else echo '✅ All strings translated.'; fi",
|
||||
"check:all": "pnpm check:optional-chaining && pnpm check:translations",
|
||||
"build-check": "pnpm build && pnpm build-web && pnpm check:all"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.735.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.735.0",
|
||||
"@ducanh2912/next-pwa": "^10.2.9",
|
||||
"@fabianlars/tauri-plugin-oauth": "2",
|
||||
"@opennextjs/cloudflare": "^1.1.0",
|
||||
"@opennextjs/cloudflare": "^1.6.1",
|
||||
"@stripe/react-stripe-js": "^3.7.0",
|
||||
"@stripe/stripe-js": "^7.4.0",
|
||||
"@supabase/auth-ui-react": "^0.4.7",
|
||||
"@supabase/auth-ui-shared": "^0.1.8",
|
||||
"@supabase/supabase-js": "^2.49.10",
|
||||
"@tauri-apps/api": "2.5.0",
|
||||
"@tauri-apps/plugin-cli": "^2.2.0",
|
||||
"@tauri-apps/plugin-deep-link": "^2.2.1",
|
||||
"@tauri-apps/plugin-dialog": "^2.2.1",
|
||||
"@tauri-apps/plugin-fs": "^2.2.1",
|
||||
"@tauri-apps/plugin-haptics": "^2.2.4",
|
||||
"@tauri-apps/plugin-http": "^2.4.3",
|
||||
"@tauri-apps/plugin-log": "^2.4.0",
|
||||
"@tauri-apps/plugin-opener": "^2.2.6",
|
||||
"@tauri-apps/plugin-os": "^2.2.1",
|
||||
"@tauri-apps/plugin-process": "^2.2.1",
|
||||
"@tauri-apps/plugin-shell": "~2.2.1",
|
||||
"@tauri-apps/plugin-updater": "^2.7.1",
|
||||
"@supabase/supabase-js": "^2.50.2",
|
||||
"@tauri-apps/api": "2.6.0",
|
||||
"@tauri-apps/plugin-cli": "^2.4.0",
|
||||
"@tauri-apps/plugin-deep-link": "^2.4.0",
|
||||
"@tauri-apps/plugin-dialog": "^2.3.0",
|
||||
"@tauri-apps/plugin-fs": "^2.4.0",
|
||||
"@tauri-apps/plugin-haptics": "^2.3.0",
|
||||
"@tauri-apps/plugin-http": "^2.5.0",
|
||||
"@tauri-apps/plugin-log": "^2.6.0",
|
||||
"@tauri-apps/plugin-opener": "^2.4.0",
|
||||
"@tauri-apps/plugin-os": "^2.3.0",
|
||||
"@tauri-apps/plugin-process": "^2.3.0",
|
||||
"@tauri-apps/plugin-shell": "~2.3.0",
|
||||
"@tauri-apps/plugin-updater": "^2.9.0",
|
||||
"@zip.js/zip.js": "^2.7.53",
|
||||
"app-store-server-api": "^0.17.1",
|
||||
"aws4fetch": "^1.0.20",
|
||||
"clsx": "^2.1.1",
|
||||
"cors": "^2.8.5",
|
||||
"cssbeautify": "^0.3.1",
|
||||
"dayjs": "^1.11.13",
|
||||
"foliate-js": "workspace:*",
|
||||
"franc-min": "^6.2.0",
|
||||
"highlight.js": "^11.11.1",
|
||||
"i18next": "^24.2.0",
|
||||
"i18next-browser-languagedetector": "^8.0.2",
|
||||
"i18next-http-backend": "^3.0.1",
|
||||
"iso-639-2": "^3.0.2",
|
||||
"iso-639-3": "^3.0.1",
|
||||
"js-md5": "^0.8.3",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"marked": "^15.0.12",
|
||||
"next": "15.3.3",
|
||||
"overlayscrollbars": "^2.11.4",
|
||||
"overlayscrollbars-react": "^0.5.6",
|
||||
"posthog-js": "^1.246.0",
|
||||
"react": "19.0.0",
|
||||
"react-color": "^2.19.3",
|
||||
@@ -81,21 +96,29 @@
|
||||
"react-responsive": "^10.0.0",
|
||||
"react-window": "^1.8.11",
|
||||
"semver": "^7.7.1",
|
||||
"stripe": "^18.2.1",
|
||||
"tinycolor2": "^1.6.0",
|
||||
"zustand": "5.0.1"
|
||||
"uuid": "^11.1.0",
|
||||
"zod": "^4.0.8",
|
||||
"zustand": "5.0.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@next/bundle-analyzer": "^15.4.2",
|
||||
"@tailwindcss/typography": "^0.5.16",
|
||||
"@tauri-apps/cli": "2.5.0",
|
||||
"@tauri-apps/cli": "2.7.0",
|
||||
"@testing-library/dom": "^10.4.0",
|
||||
"@testing-library/react": "^16.3.0",
|
||||
"@types/cors": "^2.8.17",
|
||||
"@types/cssbeautify": "^0.3.5",
|
||||
"@types/node": "^22.10.1",
|
||||
"@types/node": "^22.15.31",
|
||||
"@types/react": "18.3.12",
|
||||
"@types/react-color": "^3.0.13",
|
||||
"@types/react-dom": "18.3.1",
|
||||
"@types/react-window": "^1.8.8",
|
||||
"@types/semver": "^7.7.0",
|
||||
"@types/tinycolor2": "^1.4.6",
|
||||
"@types/uuid": "^10.0.0",
|
||||
"@vitejs/plugin-react": "^4.7.0",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"cpx2": "^8.0.0",
|
||||
"daisyui": "^4.12.24",
|
||||
@@ -103,6 +126,7 @@
|
||||
"eslint": "^9.16.0",
|
||||
"eslint-config-next": "15.0.3",
|
||||
"i18next-scanner": "^4.6.0",
|
||||
"jsdom": "^26.1.0",
|
||||
"mkdirp": "^3.0.1",
|
||||
"node-env-run": "^4.0.2",
|
||||
"postcss": "^8.4.49",
|
||||
@@ -111,6 +135,8 @@
|
||||
"raw-loader": "^4.0.2",
|
||||
"tailwindcss": "^3.4.17",
|
||||
"typescript": "^5.7.2",
|
||||
"wrangler": "^4.19.1"
|
||||
"vite-tsconfig-paths": "^5.1.4",
|
||||
"vitest": "^3.2.4",
|
||||
"wrangler": "^4.26.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
[
|
||||
{
|
||||
"relation": ["delegate_permission/common.handle_all_urls"],
|
||||
"relation": [
|
||||
"delegate_permission/common.handle_all_urls",
|
||||
"delegate_permission/common.get_login_creds"
|
||||
],
|
||||
"target": {
|
||||
"namespace": "android_app",
|
||||
"package_name": "com.bilingify.readest",
|
||||
"sha256_cert_fingerprints": [
|
||||
"65:2D:11:67:76:12:29:14:18:42:CB:3D:18:50:B6:E4:7E:46:E1:2F:4B:E4:7F:5A:6C:14:B6:D7:12:74:1E:82"
|
||||
"65:2D:11:67:76:12:29:14:18:42:CB:3D:18:50:B6:E4:7E:46:E1:2F:4B:E4:7F:5A:6C:14:B6:D7:12:74:1E:82",
|
||||
"E0:E7:60:55:80:8D:3A:DE:A0:D1:CF:7C:20:85:40:A3:DD:4B:E6:4D:17:5C:0F:DE:26:57:7D:9C:5B:29:5F:51"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
"Confirm Deletion": "تأكيد الحذف",
|
||||
"Copied to notebook": "تم النسخ إلى المفكرة",
|
||||
"Copy": "نسخ",
|
||||
"Custom CSS": "CSS مخصص",
|
||||
"Dark Mode": "الوضع الداكن",
|
||||
"Default": "الافتراضي",
|
||||
"Default Font": "الخط الافتراضي",
|
||||
@@ -28,7 +27,6 @@
|
||||
"Dictionary": "القاموس",
|
||||
"Download Readest": "تحميل ريديست",
|
||||
"Edit": "تحرير",
|
||||
"Enter your custom CSS here...": "أدخل الـ CSS المخصص هنا...",
|
||||
"Excerpts": "مقتطفات",
|
||||
"Failed to import book(s): {{filenames}}": "فشل استيراد الكتاب/الكتب: {{filenames}}",
|
||||
"Fast": "سريع",
|
||||
@@ -48,7 +46,6 @@
|
||||
"Horizontal Direction": "الاتجاه الأفقي",
|
||||
"Hyphenation": "الواصلة",
|
||||
"Import Books": "استيراد الكتب",
|
||||
"Language:": "اللغة:",
|
||||
"Layout": "التخطيط",
|
||||
"Light Mode": "الوضع الفاتح",
|
||||
"Loading...": "جارٍ التحميل...",
|
||||
@@ -69,9 +66,8 @@
|
||||
"Page": "الصفحة",
|
||||
"Paging Animation": "رسوم متحركةعند الانتقال إلى صفحة جديدة",
|
||||
"Paragraph": "فقرة",
|
||||
"Parallel Read": "قراءة متوازية",
|
||||
"Published:": "تاريخ النشر:",
|
||||
"Publisher:": "الناشر:",
|
||||
"Published": "تاريخ النشر",
|
||||
"Publisher": "الناشر",
|
||||
"Reading Progress Synced": "تمت مزامنة تقدم القراءة",
|
||||
"Reload Page": "إعادة تحميل الصفحة",
|
||||
"Reveal in File Explorer": "إظهار في مستكشف الملفات",
|
||||
@@ -96,16 +92,15 @@
|
||||
"Slow": "بطيء",
|
||||
"Solarized": "سولاريزد",
|
||||
"Speak": "تحدث",
|
||||
"Subjects:": "المواضيع:",
|
||||
"Subjects": "المواضيع",
|
||||
"System Fonts": "خطوط النظام",
|
||||
"TTS not supported in this device": "القراءة الصوتية غير مدعومة في هذا الجهاز",
|
||||
"Theme Color": "لون السمة",
|
||||
"Theme Mode": "وضع السمة",
|
||||
"Translate": "ترجمة",
|
||||
"Translated Text": "النص المترجم",
|
||||
"Unknown": "غير معروف",
|
||||
"Untitled": "بدون عنوان",
|
||||
"Updated:": "تم التحديث:",
|
||||
"Updated": "تم التحديث",
|
||||
"Version {{version}}": "الإصدار {{version}}",
|
||||
"Vertical Direction": "الاتجاه العمودي",
|
||||
"Welcome to your library. You can import your books here and read them anytime.": "مرحبًا بكم في مكتبتكم. يمكنكم استيراد كتبكم هنا وقراءتها في أي وقت.",
|
||||
@@ -114,8 +109,6 @@
|
||||
"Your Library": "المكتبة الخاصة بك",
|
||||
"TTS not supported for PDF": "القراءة الصوتية غير مدعومة لملفات PDF",
|
||||
"Override Book Font": "تجاوز خط الكتاب",
|
||||
"Vertical Margins (px)": "هوامش عمودية (بكسل)",
|
||||
"Horizontal Margins (%)": "هوامش أفقية (%)",
|
||||
"Apply to All Books": "تطبيق على جميع الكتب",
|
||||
"Apply to This Book": "تطبيق على هذا الكتاب",
|
||||
"Unable to fetch the translation. Try again later.": "تعذر جلب الترجمة. حاول مرة أخرى لاحقًا.",
|
||||
@@ -130,8 +123,6 @@
|
||||
"Failed to download book: {{title}}": "فشل تنزيل الكتاب: {{title}}",
|
||||
"Upload Book": "تحميل الكتاب",
|
||||
"Auto Upload Books to Cloud": "تحميل الكتب تلقائيًا إلى السحابة",
|
||||
"{{percentage}}% of Cloud Storage Used.": "تم استخدام {{percentage}}% من مساحة التخزين السحابية.",
|
||||
"Storage": "التخزين",
|
||||
"Book deleted: {{title}}": "تم حذف الكتاب: {{title}}",
|
||||
"Failed to delete book: {{title}}": "فشل حذف الكتاب: {{title}}",
|
||||
"Check Updates on Start": "التحقق من التحديثات عند البدء",
|
||||
@@ -163,7 +154,7 @@
|
||||
"Create a Password": "إنشاء كلمة مرور",
|
||||
"Sign up": "إنشاء حساب",
|
||||
"Signing up...": "يتم الآن إنشاء الحساب...",
|
||||
"Don’t have an account? Sign up": "ليس لديك حساب؟ قم بالتسجيل",
|
||||
"Don't have an account? Sign up": "ليس لديك حساب؟ قم بالتسجيل",
|
||||
"Check your email for the confirmation link": "تحقق من بريدك الإلكتروني للحصول على رابط التأكيد",
|
||||
"Signing in ...": "يتم الآن تسجيل الدخول ...",
|
||||
"Send a magic link email": "إرسال رابط سحري إلى بريدك الإلكتروني",
|
||||
@@ -187,35 +178,13 @@
|
||||
"Sign in with GitHub": "تسجيل الدخول عبر GitHub",
|
||||
"Account": "الحساب",
|
||||
"Failed to delete user. Please try again later.": "فشل حذف المستخدم. يرجى المحاولة مرة أخرى لاحقًا.",
|
||||
"Unlimited Offline/Online Reading": "القراءة بدون حدود للكتب المتوفر على الجهاز أو عبر الانترنت",
|
||||
"Unlimited Cloud Sync Devices": "مزامنة عبر السحابة لعدد غير محدود من الأجهزة",
|
||||
"Essential Text-to-Speech Voices": "أصوات أساسية لتحويل النص إلى كلام",
|
||||
"DeepL Free Access": "وصول مجاني لـ DeepL",
|
||||
"Community Support": "دعم المجتمع",
|
||||
"500 MB Cloud Sync Space": "مزامنة سحابية بمساحة تخزين 500 ميجابايت",
|
||||
"Includes All Free Tier Benefits": "تشمل جميع مزايا الفئة المجانية",
|
||||
"AI Summaries": "ملخصات الذكاء الاصطناعي",
|
||||
"AI Translations": "ترجمات الذكاء الاصطناعي",
|
||||
"Priority Support": "دعم ذو أولوية",
|
||||
"DeepL Pro Access": "وصول لميزات DeepL Pro",
|
||||
"Expanded Text-to-Speech Voices": "أصوات موسعة لتحويل النص إلى كلام",
|
||||
"2000 MB Cloud Sync Space": "مزامنة سحابية بمساحة تخزين 2000 ميجابايت",
|
||||
"Includes All Plus Tier Benefits": "تشمل جميع مزايا فئة Plus",
|
||||
"Unlimited AI Summaries": "ملخصات ذكاء اصطناعي غير محدودة",
|
||||
"Unlimited AI Translations": "ترجمات ذكاء اصطناعي غير محدودة",
|
||||
"Unlimited DeepL Pro Access": "وصول غير محدود لميزات DeepL Pro",
|
||||
"Advanced AI Tools": "أدوات ذكاء اصطناعي متقدمة",
|
||||
"Early Feature Access": "وصول مبكر للميزات",
|
||||
"10 GB Cloud Sync Space": "مزامنة سحابية بمساحة تخزين 10 جيجابايت",
|
||||
"Loading profile...": "جارٍ تحميل الملف الشخصي...",
|
||||
"Features": "الميزات",
|
||||
"Delete Account": "حذف الحساب",
|
||||
"Delete Your Account?": "حذف حسابك؟",
|
||||
"This action cannot be undone. All your data in the cloud will be permanently deleted.": "لا يمكن التراجع عن هذا الإجراء. سيتم حذف جميع بياناتك في السحابة بشكل دائم.",
|
||||
"Delete Permanently": "حذف نهائي",
|
||||
"Free Tier": "الفئة المجانية",
|
||||
"Plus Tier": "فئة Plus",
|
||||
"Pro Tier": "فئة Pro",
|
||||
"RTL Direction": "الاتجاه من اليمين إلى اليسار",
|
||||
"Maximum Column Height": "الارتفاع الأقصى للعمود",
|
||||
"Maximum Column Width": "العرض الأقصى للعمود",
|
||||
@@ -266,13 +235,13 @@
|
||||
"Clear Search": "مسح البحث",
|
||||
"Header & Footer": "الترويسة والتذييل",
|
||||
"Apply also in Scrolled Mode": "تطبيق أيضًا في وضع التمرير",
|
||||
"A new version of Readest is Available!": "يتوفر إصدار جديد من Readest!",
|
||||
"A new version of Readest is available!": "يتوفر إصدار جديد من Readest!",
|
||||
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "إصدار {{newVersion}} من ريديست متاح للتنزيل (الإصدار المثبت حاليًا {{currentVersion}}).",
|
||||
"Download and install now?": "هل ترغب في تنزيله وتثبيته الآن؟",
|
||||
"Downloading {{downloaded}} of {{contentLength}}": "جارٍ تنزيل {{downloaded}} من {{contentLength}}",
|
||||
"Download finished": "اكتمل التنزيل",
|
||||
"DOWNLOAD & INSTALL": "تنزيل وتثبيت",
|
||||
"Changelog:": "سجل التغييرات:",
|
||||
"Changelog": "سجل التغييرات",
|
||||
"Software Update": "تحديث البرنامج",
|
||||
"Title": "العنوان",
|
||||
"Date Read": "تاريخ القراءة",
|
||||
@@ -281,10 +250,9 @@
|
||||
"Ascending": "تصاعدي",
|
||||
"Descending": "تنازلي",
|
||||
"Sort by...": "ترتيب حسب...",
|
||||
"Added:": "تاريخ الإضافة:",
|
||||
"Format:": "التنسيق:",
|
||||
"Added": "تاريخ الإضافة",
|
||||
"GuanKiapTsingKhai-T": "GuanKiapTsingKhai-T",
|
||||
"Description:": "الوصف:",
|
||||
"Description": "الوصف",
|
||||
"No description available": "لا يوجد وصف متاح",
|
||||
"List": "قائمة",
|
||||
"Grid": "شبكة",
|
||||
@@ -301,7 +269,7 @@
|
||||
"Checking for updates...": "جارٍ التحقق من التحديثات...",
|
||||
"Error checking for updates": "خطأ في التحقق من التحديثات",
|
||||
"Details": "التفاصيل",
|
||||
"File Size:": "حجم الملف:",
|
||||
"File Size": "حجم الملف",
|
||||
"Auto Detect": "الكشف التلقائي",
|
||||
"Next Section": "القسم التالي",
|
||||
"Previous Section": "القسم السابق",
|
||||
@@ -350,14 +318,12 @@
|
||||
"Synced at {{time}}": "تمت المزامنة في {{time}}",
|
||||
"Never synced": "لم تتم المزامنة بعد",
|
||||
"Show Remaining Time": "إظهار الوقت المتبقي",
|
||||
"Show Page Number": "إظهار رقم الصفحة",
|
||||
"{{time}} min left in chapter": "{{time}} دقيقة متبقية حتى نهاية الفصل",
|
||||
"Override Book Color": "تجاوز لون الكتاب",
|
||||
"Login Required": "يتطلب الأمر تسجيل الدخول",
|
||||
"Quota Exceeded": "تجاوزت الحصة المخصصة",
|
||||
"{{percentage}}% of Daily Translation Characters Used.": "تم استخدام ما نسبته {{percentage}}% من إجمالي أحرف الترجمة المسموح بها يوميًا.",
|
||||
"Translation Characters": "أحرف الترجمة",
|
||||
"Daily translation quota reached. Select another translate service to proceed.": "تم استنفاد حصة الترجمة اليومية. قم بتحديد خدمة ترجمة أخرى للاستمرار.",
|
||||
"{{engine}}: {{count}} voices_zero": "{{engine}}: لا توجد أصوات",
|
||||
"{{engine}}: {{count}} voices_one": "{{engine}}: صوت واحد",
|
||||
"{{engine}}: {{count}} voices_two": "{{engine}}: صوتان",
|
||||
@@ -371,5 +337,188 @@
|
||||
"Contact Support": "اتصل بالدعم الفني",
|
||||
"Code Highlighting": "تمييز الكود",
|
||||
"Enable Highlighting": "تمكين التمييز",
|
||||
"Code Language": "لغة الكود"
|
||||
"Code Language": "لغة الكود",
|
||||
"Top Margin (px)": "الهامش العلوي (بكسل)",
|
||||
"Bottom Margin (px)": "الهامش السفلي (بكسل)",
|
||||
"Right Margin (px)": "الهامش الأيمن (بكسل)",
|
||||
"Left Margin (px)": "الهامش الأيسر (بكسل)",
|
||||
"Column Gap (%)": "تباعد الأعمدة (%)",
|
||||
"Always Show Status Bar": "دائمًا إظهار شريط الحالة",
|
||||
"Translation Not Available": "الترجمة غير متاحة",
|
||||
"Custom Content CSS": "CSS مخصص للمحتوى",
|
||||
"Enter CSS for book content styling...": "أدخل CSS لتنسيق محتوى الكتاب...",
|
||||
"Custom Reader UI CSS": "CSS مخصص لواجهة القارئ",
|
||||
"Enter CSS for reader interface styling...": "أدخل CSS لتنسيق واجهة القارئ...",
|
||||
"Crop": "قص",
|
||||
"Book Covers": "أغلفة الكتب",
|
||||
"Fit": "ملاءمة",
|
||||
"Reset {{settings}}": "إعادة تعيين {{settings}}",
|
||||
"Reset Settings": "إعادة تعيين الإعدادات",
|
||||
"{{count}} pages left in chapter_zero": "لم يتبق أي صفحات في هذا الفصل",
|
||||
"{{count}} pages left in chapter_one": "تبقّت صفحة واحدة في هذا الفصل",
|
||||
"{{count}} pages left in chapter_two": "تبقّت صفحتان في هذا الفصل",
|
||||
"{{count}} pages left in chapter_few": "تبقّت {{count}} صفحات في هذا الفصل",
|
||||
"{{count}} pages left in chapter_many": "تبقّت {{count}} صفحة في هذا الفصل",
|
||||
"{{count}} pages left in chapter_other": "تبقّى {{count}} صفحة في هذا الفصل",
|
||||
"Show Remaining Pages": "عرض الصفحات المتبقية",
|
||||
"Source Han Serif CN VF": "Source Han Serif",
|
||||
"Huiwen-mincho": "Huiwen Mincho",
|
||||
"KingHwa_OldSong": "KingHwa Song",
|
||||
"Manage Subscription": "إدارة الاشتراك",
|
||||
"Coming Soon": "قريبًا",
|
||||
"Upgrade to {{plan}}": "الترقية إلى {{plan}}",
|
||||
"Upgrade to Plus or Pro": "الترقية إلى Plus أو Pro",
|
||||
"Current Plan": "الخطة الحالية",
|
||||
"Plan Limits": "حدود الخطة",
|
||||
"Processing your payment...": "جارٍ معالجة الدفع...",
|
||||
"Please wait while we confirm your subscription.": "يرجى الانتظار بينما نقوم بتأكيد اشتراكك.",
|
||||
"Payment Processing": "معالجة الدفع",
|
||||
"Your payment is being processed. This usually takes a few moments.": "يتم الآن معالجة الدفع الخاص بك. عادةً ما يستغرق ذلك بضع لحظات.",
|
||||
"Payment Failed": "فشل الدفع",
|
||||
"We couldn't process your subscription. Please try again or contact support if the issue persists.": "تعذر علينا معالجة اشتراكك. يرجى المحاولة مرة أخرى أو التواصل مع الدعم إذا استمرت المشكلة.",
|
||||
"Back to Profile": "العودة إلى الملف الشخصي",
|
||||
"Subscription Successful!": "تم الاشتراك بنجاح!",
|
||||
"Thank you for your subscription! Your payment has been processed successfully.": "شكرًا لاشتراكك! تم معالجة الدفع بنجاح.",
|
||||
"Email:": "البريد الإلكتروني:",
|
||||
"Plan:": "الخطة:",
|
||||
"Amount:": "المبلغ:",
|
||||
"Subscription ID:": "رقم الاشتراك:",
|
||||
"Go to Library": "الانتقال إلى المكتبة",
|
||||
"Need help? Contact our support team at support@readest.com": "تحتاج مساعدة؟ تواصل مع فريق الدعم على support@readest.com",
|
||||
"Free Plan": "الخطة المجانية",
|
||||
"month": "شهر",
|
||||
"AI Translations (per day)": "ترجمات بالذكاء الاصطناعي (يوميًا)",
|
||||
"Plus Plan": "خطة Plus",
|
||||
"Includes All Free Plan Benefits": "تتضمن جميع مزايا الخطة المجانية",
|
||||
"Pro Plan": "خطة Pro",
|
||||
"More AI Translations": "المزيد من الترجمات بالذكاء الاصطناعي",
|
||||
"Complete Your Subscription": "أكمل اشتراكك",
|
||||
"{{percentage}}% of Cloud Sync Space Used.": "تم استخدام {{percentage}}% من مساحة المزامنة السحابية.",
|
||||
"Cloud Sync Storage": "مساحة المزامنة السحابية",
|
||||
"Parallel Read": "القراءة المتزامنة",
|
||||
"Disable": "تعطيل",
|
||||
"Enable": "تمكين",
|
||||
"Upgrade to Readest Premium": "ترقية إلى Readest Premium",
|
||||
"Show Source Text": "عرض النص المصدر",
|
||||
"Cross-Platform Sync": "مزامنة عبر الأجهزة",
|
||||
"Seamlessly sync your library, progress, highlights, and notes across all your devices—never lose your place again.": "زامن مكتبتك وتقدمك وملاحظاتك عبر جميع أجهزتك بسهولة.",
|
||||
"Customizable Reading": "قراءة قابلة للتخصيص",
|
||||
"Personalize every detail with adjustable fonts, layouts, themes, and advanced display settings for the perfect reading experience.": "خصص الخطوط والتنسيقات والألوان لتجربة قراءة مثالية.",
|
||||
"AI Read Aloud": "القراءة بالصوت عبر الذكاء الاصطناعي",
|
||||
"Enjoy hands-free reading with natural-sounding AI voices that bring your books to life.": "استمع للكتب بأصوات طبيعية بدون استخدام اليدين.",
|
||||
"AI Translations": "ترجمة ذكية",
|
||||
"Connect with fellow readers and get help fast in our friendly community channels.": "تواصل مع القراء واحصل على الدعم بسرعة.",
|
||||
"Unlimited AI Read Aloud Hours": "ساعات قراءة غير محدودة",
|
||||
"Listen without limits—convert as much text as you like into immersive audio.": "استمع بلا حدود وحوّل النص لصوت بسهولة.",
|
||||
"Unlock enhanced translation capabilities with more daily usage and advanced options.": "استمتع بترجمة موسعة وخيارات متقدمة.",
|
||||
"DeepL Pro Access": "وصول DeepL Pro",
|
||||
"Enjoy faster responses and dedicated assistance whenever you need help.": "احصل على دعم سريع ومخصص عند الحاجة.",
|
||||
"Daily translation quota reached. Upgrade your plan to continue using AI translations.": "تم استهلاك حصة الترجمة اليومية. قم بالترقية للاستمرار.",
|
||||
"Translate any text instantly with the power of Google, Azure, or DeepL—understand content in any language.": "ترجم أي نص فورًا بقوة Google أو Azure أو DeepL—افهم المحتوى بأي لغة.",
|
||||
"Includes All Plus Plan Benefits": "يشمل جميع مزايا خطة بلس",
|
||||
"Early Feature Access": "الوصول المبكر للميزات",
|
||||
"Be the first to explore new features, updates, and innovations before anyone else.": "كن أول من يستكشف الميزات والتحديثات والابتكارات الجديدة.",
|
||||
"Advanced AI Tools": "أدوات الذكاء الاصطناعي المتقدمة",
|
||||
"Harness powerful AI tools for smarter reading, translation, and content discovery.": "استخدم أدوات الذكاء الاصطناعي القوية للقراءة الذكية والترجمة واكتشاف المحتوى.",
|
||||
"Translate up to 100,000 characters daily with the most accurate translation engine available.": "ترجم حتى 100 ألف حرف يوميًا بأدق محرك ترجمة متاح.",
|
||||
"Translate up to 500,000 characters daily with the most accurate translation engine available.": "ترجم حتى 500 ألف حرف يوميًا بأدق محرك ترجمة متاح.",
|
||||
"Securely store and access your entire reading collection with up to 5 GB of cloud storage.": "احفظ واوصل لمجموعة قراءتك كاملة بـ 5 جيجابايت تخزين سحابي آمن.",
|
||||
"Securely store and access your entire reading collection with up to 20 GB of cloud storage.": "احفظ واوصل لمجموعة قراءتك كاملة بـ 20 جيجابايت تخزين سحابي آمن.",
|
||||
"Deleted cloud backup of the book: {{title}}": "تم حذف النسخة الاحتياطية السحابية للكتاب: {{title}}",
|
||||
"Are you sure to delete the cloud backup of the selected book?": "هل أنت متأكد من حذف النسخة الاحتياطية السحابية للكتاب المحدد؟",
|
||||
"What's New in Readest": "ما الجديد في Readest",
|
||||
"Enter book title": "أدخل عنوان الكتاب",
|
||||
"Subtitle": "العنوان الفرعي",
|
||||
"Enter book subtitle": "أدخل العنوان الفرعي للكتاب",
|
||||
"Enter author name": "أدخل اسم المؤلف",
|
||||
"Series": "السلسلة",
|
||||
"Enter series name": "أدخل اسم السلسلة",
|
||||
"Series Index": "رقم الكتاب في السلسلة",
|
||||
"Enter series index": "أدخل رقم الكتاب في السلسلة",
|
||||
"Total in Series": "العدد الكلي في السلسلة",
|
||||
"Enter total books in series": "أدخل العدد الكلي للكتب في السلسلة",
|
||||
"Enter publisher": "أدخل اسم الناشر",
|
||||
"Publication Date": "تاريخ النشر",
|
||||
"Identifier": "المعرف",
|
||||
"Enter book description": "أدخل وصف الكتاب",
|
||||
"Change cover image": "تغيير صورة الغلاف",
|
||||
"Replace": "استبدال",
|
||||
"Unlock cover": "إلغاء قفل الغلاف",
|
||||
"Lock cover": "قفل الغلاف",
|
||||
"Auto-Retrieve Metadata": "استرجاع البيانات الوصفية تلقائياً",
|
||||
"Auto-Retrieve": "استرجاع تلقائي",
|
||||
"Unlock all fields": "إلغاء قفل جميع الحقول",
|
||||
"Unlock All": "إلغاء قفل الكل",
|
||||
"Lock all fields": "قفل جميع الحقول",
|
||||
"Lock All": "قفل الكل",
|
||||
"Reset": "إعادة تعيين",
|
||||
"Edit Metadata": "تحرير البيانات الوصفية",
|
||||
"Locked": "مقفل",
|
||||
"Select Metadata Source": "اختر مصدر البيانات الوصفية",
|
||||
"Keep manual input": "الاحتفاظ بالإدخال اليدوي",
|
||||
"Google Books": "كتب جوجل",
|
||||
"Open Library": "المكتبة المفتوحة",
|
||||
"Fiction, Science, History": "خيال، علوم، تاريخ",
|
||||
"Select Cover Image": "اختر صورة الغلاف",
|
||||
"Open Book in New Window": "فتح الكتاب في نافذة جديدة",
|
||||
"Voices for {{lang}}": "الأصوات لـ {{lang}}",
|
||||
"Yandex Translate": "ترجمة Yandex",
|
||||
"YYYY or YYYY-MM-DD": "YYYY أو YYYY-MM-DD",
|
||||
"Restore Purchase": "استعادة الشراء",
|
||||
"No purchases found to restore.": "لا توجد مشتريات لاستعادتها.",
|
||||
"Failed to restore purchases.": "فشل في استعادة المشتريات.",
|
||||
"Failed to manage subscription.": "فشل في إدارة الاشتراك.",
|
||||
"Failed to load subscription plans.": "فشل في تحميل خطط الاشتراك.",
|
||||
"year": "سنة",
|
||||
"Failed to create checkout session": "فشل في إنشاء جلسة الدفع",
|
||||
"Storage": "التخزين",
|
||||
"Terms of Service": "شروط الخدمة",
|
||||
"Privacy Policy": "سياسة الخصوصية",
|
||||
"Disable Double Click": "تعطيل النقر المزدوج",
|
||||
"TTS not supported for this document": "تكنولوجيا تحويل النص إلى كلام غير مدعومة لهذا المستند.",
|
||||
"Reset Password": "إعادة تعيين",
|
||||
"Show Reading Progress": "إظهار تقدم القراءة",
|
||||
"Reading Progress Style": "نمط تقدم القراءة",
|
||||
"Page Number": "رقم الصفحة",
|
||||
"Percentage": "النسبة المئوية",
|
||||
"Deleted local copy of the book: {{title}}": "تم حذف النسخة المحلية للكتاب: {{title}}",
|
||||
"Failed to delete cloud backup of the book: {{title}}": "فشل في حذف النسخة الاحتياطية السحابية للكتاب: {{title}}",
|
||||
"Failed to delete local copy of the book: {{title}}": "فشل في حذف النسخة المحلية للكتاب: {{title}}",
|
||||
"Are you sure to delete the local copy of the selected book?": "هل أنت متأكد من حذف النسخة المحلية للكتاب المحدد؟",
|
||||
"Remove from Cloud & Device": "إزالة من السحابة والجهاز",
|
||||
"Remove from Cloud Only": "إزالة من السحابة فقط",
|
||||
"Remove from Device Only": "إزالة من الجهاز فقط",
|
||||
"Disconnected": "غير متصل",
|
||||
"KOReader Sync Settings": "إعدادات مزامنة KOReader",
|
||||
"Sync Strategy": "استراتيجية المزامنة",
|
||||
"Ask on conflict": "اسأل عند حدوث تعارض",
|
||||
"Always use latest": "استخدام الأحدث دائمًا",
|
||||
"Send changes only": "إرسال التغييرات فقط",
|
||||
"Receive changes only": "استلام التغييرات فقط",
|
||||
"Disabled": "معطل",
|
||||
"Checksum Method": "طريقة التحقق من الصحة",
|
||||
"File Content (recommended)": "محتوى الملف (موصى به)",
|
||||
"File Name": "اسم الملف",
|
||||
"Device Name": "اسم الجهاز",
|
||||
"Sync Tolerance": "تحمل المزامنة",
|
||||
"Connect to your KOReader Sync server.": "الاتصال بخادم مزامنة KOReader الخاص بك.",
|
||||
"Server URL": "عنوان URL للخادم",
|
||||
"Username": "اسم المستخدم",
|
||||
"Your Username": "اسم المستخدم الخاص بك",
|
||||
"Password": "كلمة المرور",
|
||||
"Connect": "الاتصال",
|
||||
"KOReader Sync": "مزامنة KOReader",
|
||||
"Sync Conflict": "تعارض المزامنة",
|
||||
"Sync reading progress from \"{{deviceName}}\"?": "مزامنة تقدم القراءة من \"{{deviceName}}\"؟",
|
||||
"another device": "جهاز آخر",
|
||||
"Local Progress": "التقدم المحلي",
|
||||
"Remote Progress": "التقدم البعيد",
|
||||
"Page {{page}} of {{total}} ({{percentage}}%)": "الصفحة {{page}} من {{total}} ({{percentage}}%)",
|
||||
"Current position": "الموقع الحالي",
|
||||
"Approximately page {{page}} of {{total}} ({{percentage}}%)": "تقريبًا الصفحة {{page}} من {{total}} ({{percentage}}%)",
|
||||
"Approximately {{percentage}}%": "تقريبًا {{percentage}}%",
|
||||
"Failed to connect": "فشل في الاتصال",
|
||||
"Sync Server Connected": "تم الاتصال بخادم المزامنة",
|
||||
"Precision: {{precision}} digits after the decimal": "الدقة: {{precision}} أرقام بعد الفاصلة",
|
||||
"Sync as {{userDisplayName}}": "المزامنة كـ {{userDisplayName}}"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,504 @@
|
||||
{
|
||||
"(detected)": "(བརྟག་དཔྱད་བྱུང་བ།)",
|
||||
"About Readest": "Readest སྐོར།",
|
||||
"Add your notes here...": "འདིར་ཁྱེད་ཀྱི་ཟིན་བྲིས་འདེབས་དང་།...",
|
||||
"Animation": "འགུལ་རིས།",
|
||||
"Annotate": "ཟིན་བྲིས།",
|
||||
"Apply": "སྤྱོད་པ།",
|
||||
"Auto Mode": "རང་འགུལ་བརྗོད་བྱ།",
|
||||
"Behavior": "བྱ་སྤྱོད།",
|
||||
"Book": "དཔེ་དེབ།",
|
||||
"Book Cover": "དཔེ་དེབ་ཀྱི་སྟོང་ངོས།",
|
||||
"Bookmark": "དཔེ་རྟགས།",
|
||||
"Cancel": "འདོར་བ།",
|
||||
"Chapter": "ལེའུ།",
|
||||
"Cherry": "ཆུ་འབྲས་མདོག",
|
||||
"Color": "ཚོན་མདོག",
|
||||
"Confirm": "གཏན་འཁེལ།",
|
||||
"Confirm Deletion": "བསུབ་པ་གཏན་འཁེལ།",
|
||||
"Copied to notebook": "ཟིན་དེབ་ཏུ་འདྲ་བཤུས་བྱས་ཟིན།",
|
||||
"Copy": "འདྲ་བཤུས།",
|
||||
"Dark Mode": "མདོག་ནག་པོའི་བརྗོད་བྱ།",
|
||||
"Default": "སྔོན་སྒྲིག",
|
||||
"Default Font": "སྔོན་སྒྲིག་ཡིག་གཟུགས།",
|
||||
"Default Font Size": "སྔོན་སྒྲིག་ཡིག་ཆེ་ཆུང་།",
|
||||
"Delete": "བསུབ་པ།",
|
||||
"Delete Highlight": "འོག་ཐིག་བསུབ་པ།",
|
||||
"Dictionary": "ཚིག་མཛོད།",
|
||||
"Download Readest": "Readest ཕབ་ལེན།",
|
||||
"Edit": "རྩོམ་སྒྲིག",
|
||||
"Excerpts": "དྲངས་བཏུས།",
|
||||
"Failed to import book(s): {{filenames}}": "དཔེ་དེབ་ནང་འདྲེན་བྱས་མ་ཐུབ། {{filenames}}",
|
||||
"Fast": "མགྱོགས་པོ།",
|
||||
"Font": "ཡིག་གཟུགས།",
|
||||
"Font & Layout": "ཡིག་གཟུགས་དང་བཀོད་སྒྲིག",
|
||||
"Font Face": "ཡིག་གཟུགས་ཀྱི་རྣམ་པ།",
|
||||
"Font Family": "ཡིག་རིགས།",
|
||||
"Font Size": "ཡིག་ཆེ་ཆུང་།",
|
||||
"Full Justification": "གཡས་གཡོན་སྙོམས་པ།",
|
||||
"Global Settings": "ཁྱོན་ཡོངས་སྒྲིག་བཀོད།",
|
||||
"Go Back": "ཕྱིར་ལོག",
|
||||
"Go Forward": "མདུན་སྐྱོད།",
|
||||
"Grass": "རྩྭ་ལྗང་ཁུ།",
|
||||
"Gray": "སྙིང་རུས་ཆུང་བ།",
|
||||
"Gruvbox": "དྲོད་ལྡན་གོང་མདོག",
|
||||
"Highlight": "འོག་ཐིག",
|
||||
"Horizontal Direction": "འཕྲེད་རིམ།",
|
||||
"Hyphenation": "ཡི་གེ་གཤགས་པ།",
|
||||
"Import Books": "དཔེ་དེབ་ནང་འདྲེན།",
|
||||
"Layout": "བཀོད་སྒྲིག",
|
||||
"Light Mode": "མདོག་སྐྱ་བོའི་བརྗོད་བྱ།",
|
||||
"Loading...": "འགུལ་སྐྱོང་བྱེད་བཞིན་པ།...",
|
||||
"Logged in": "ནང་བསྐྱོད་བྱས་ཟིན།",
|
||||
"Logged in as {{userDisplayName}}": "{{userDisplayName}} ནང་བསྐྱོད་བྱས་ཟིན།",
|
||||
"Match Case": "ཡི་གེ་ཆེ་ཆུང་གི་དོ་མཚུངས།",
|
||||
"Match Diacritics": "སྒྲ་གདངས་ཀྱི་དོ་མཚུངས།",
|
||||
"Match Whole Words": "ཚིག་བྱང་ཆ་ཚང་གི་དོ་མཚུངས།",
|
||||
"Maximum Number of Columns": "གྲལ་ཐིག་གི་གྲངས་ཀ།",
|
||||
"Minimum Font Size": "ཡིག་ཆུང་ཤོས།",
|
||||
"Monospace Font": "རྒྱ་ཁྱོན་འདྲ་བའི་ཡིག་གཟུགས།",
|
||||
"More Info": "ཆ་འཕྲིན་དེ་བས།",
|
||||
"Nord": "འོད་ཟེར་སྔོན་པོ།",
|
||||
"Notebook": "ཟིན་དེབ།",
|
||||
"Notes": "ཟིན་བྲིས།",
|
||||
"Open": "ཁ་ཕྱེ་བ།",
|
||||
"Original Text": "མགོ་ཡིག",
|
||||
"Page": "ཤོག་ངོས།",
|
||||
"Paging Animation": "ཤོག་ལྷེ་བསྒྱུར་བའི་འགུལ་རིས།",
|
||||
"Paragraph": "དོན་ཚན།",
|
||||
"Parallel Read": "གྲལ་སྤྲོད་ཀྱིས་ཀློག་པ།",
|
||||
"Published": "དཔེ་སྐྲུན་བྱས་པའི་ཚེས་གྲངས།",
|
||||
"Publisher": "དཔེ་སྐྲུན་ཁང་།",
|
||||
"Reading Progress Synced": "ཀློག་པའི་ཡར་འཕེལ་སྐད་མཉམ་བྱས་ཟིན།",
|
||||
"Reload Page": "ཤོག་ངོས་བསྐྱར་འགུལ་སྐྱོང་བྱ་དགོས།",
|
||||
"Reveal in File Explorer": "ཡིག་ཆ་དོ་དམ་ཆས་ནང་མངོན་པ།",
|
||||
"Reveal in Finder": "བལྟ་ཞིབ་ཆས་ནང་མངོན་པ།",
|
||||
"Reveal in Folder": "ཡིག་ཁུག་ནང་མངོན་པ།",
|
||||
"Sans-Serif Font": "མཐའ་ཐིག་མེད་པའི་ཡིག་གཟུགས།",
|
||||
"Save": "ཉར་ཚགས།",
|
||||
"Scrolled Mode": "འཁོར་རྒྱུག་རྣམ་པ།",
|
||||
"Search": "འཚོལ་བཤེར།",
|
||||
"Search Books...": "དཔེ་དེབ་འཚོལ་བཤེར།...",
|
||||
"Search...": "འཚོལ་བཤེར།...",
|
||||
"Select Book": "དཔེ་དེབ་འདེམས་པ།",
|
||||
"Select Books": "དཔེ་དེབ་འདེམས་པ།",
|
||||
"Select Multiple Books": "དཔེ་དེབ་མང་པོ་འདེམས་པ།",
|
||||
"Sepia": "རྙིང་པའི་ཉམས་འགྱུར།",
|
||||
"Serif Font": "མཐའ་ཐིག་ཡོད་པའི་ཡིག་གཟུགས།",
|
||||
"Show Book Details": "དཔེ་དེབ་ཀྱི་གནས་ཚུལ་རྒྱས་པ་མངོན་པ།",
|
||||
"Sidebar": "ཟུར་ངོས་ཀྱི་གྲལ་ཐིག",
|
||||
"Sign In": "ནང་བསྐྱོད།",
|
||||
"Sign Out": "ཕྱིར་བུད།",
|
||||
"Sky": "མཁའ་དཀར།",
|
||||
"Slow": "དལ་བ།",
|
||||
"Solarized": "ཉི་འོད།",
|
||||
"Speak": "སྒྲ་ཀློག",
|
||||
"Subjects": "བརྗོད་བྱ།",
|
||||
"System Fonts": "མ་ལག་གི་ཡིག་གཟུགས།",
|
||||
"Theme Color": "བརྗོད་བྱའི་ཚོན་མདོག",
|
||||
"Theme Mode": "བརྗོད་བྱའི་རྣམ་པ།",
|
||||
"Translate": "སྒྱུར་བཅོས།",
|
||||
"Translated Text": "སྒྱུར་ཡིག",
|
||||
"Unknown": "མི་ཤེས་པ།",
|
||||
"Untitled": "མགོ་མེད།",
|
||||
"Updated": "བསྐྱར་བརྗེའི་ཚེས་གྲངས།",
|
||||
"Version {{version}}": "ཐོན་རིམ {{version}}",
|
||||
"Vertical Direction": "ཀྲིང་རིམ།",
|
||||
"Welcome to your library. You can import your books here and read them anytime.": "དཔེ་མཛོད་སྟོང་པ་ཡིན། ཁྱེད་ཀྱིས་ཁྱེད་རང་གི་དཔེ་དེབ་ནང་འདྲེན་བྱས་ནས་དུས་དང་རྣམ་པ་ཀུན་ཏུ་ཀློག་ཐུབ།",
|
||||
"Wikipedia": "ཝེ་ཁི་པི་ཌི་ཡ།",
|
||||
"Writing Mode": "པར་སྒྲིག་གི་རྣམ་པ།",
|
||||
"Your Library": "དཔེ་མཛོད།",
|
||||
"TTS not supported for PDF": "PDF ཡིག་ཆར་ TTS རྒྱབ་སྐྱོར་མི་བྱེད།",
|
||||
"Override Book Font": "དཔེ་དེབ་ཀྱི་ཡིག་གཟུགས་བརྗེ་བ།",
|
||||
"Apply to All Books": "དཔེ་དེབ་ཡོད་རྒུར་སྤྱོད་པ།",
|
||||
"Apply to This Book": "དཔེ་དེབ་འདིར་སྤྱོད་པ།",
|
||||
"Unable to fetch the translation. Try again later.": "སྒྱུར་བཅོས་ལེན་ཐུབ་མེད། ཅུང་ཙམ་སྒུག་ནས་ཡང་བསྐྱར་ཚོད་ལྟ་བྱེད་རོགས།",
|
||||
"Check Update": "བསྐྱར་བརྗེ་བརྟག་དཔྱད།",
|
||||
"Already the latest version": "ད་ལྟ་ཐོན་རིམ་གསར་ཤོས་ཡིན།",
|
||||
"Book Details": "དཔེ་དེབ་ཀྱི་གནས་ཚུལ་རྒྱས་པ།",
|
||||
"From Local File": "ས་གནས་ཀྱི་ཡིག་ཆ་ནས་ནང་འདྲེན།",
|
||||
"TOC": "དཀར་ཆག",
|
||||
"Book uploaded: {{title}}": "དཔེ་དེབ་སྤྲད་ཟིན། {{title}}",
|
||||
"Failed to upload book: {{title}}": "དཔེ་དེབ་སྤྲད་མ་ཐུབ། {{title}}",
|
||||
"Book downloaded: {{title}}": "དཔེ་དེབ་ཕབ་ལེན་བྱས་ཟིན། {{title}}",
|
||||
"Failed to download book: {{title}}": "དཔེ་དེབ་ཕབ་ལེན་བྱས་མ་ཐུབ། {{title}}",
|
||||
"Upload Book": "དཔེ་དེབ་སྤྲད་པ།",
|
||||
"Auto Upload Books to Cloud": "དཔེ་དེབ་རང་འགུལ་གྱིས་སྤྲི་དོན་ལ་སྤྲད་པ།",
|
||||
"Book deleted: {{title}}": "དཔེ་དེབ་བསུབ་ཟིན། {{title}}",
|
||||
"Failed to delete book: {{title}}": "དཔེ་དེབ་བསུབ་མ་ཐུབ། {{title}}",
|
||||
"Check Updates on Start": "འགོ་སློང་སྐབས་བསྐྱར་བརྗེ་བརྟག་དཔྱད།",
|
||||
"Insufficient storage quota": "སྤྲི་དོན་གྱི་ཉར་ཚགས་ས་ཁོངས་མི་འདང་།",
|
||||
"Font Weight": "ཡིག་གཟུགས་ཀྱི་ལྗིད་ཚད།",
|
||||
"Line Spacing": "ལོག་བར་གྱི་བར་ཐག",
|
||||
"Word Spacing": "ཚིག་བར་གྱི་བར་ཐག",
|
||||
"Letter Spacing": "ཡི་གེ་བར་གྱི་བར་ཐག",
|
||||
"Text Indent": "ལོག་ཐོག་མའི་ནང་དུ་བསྐྱུར་བ།",
|
||||
"Paragraph Margin": "དོན་ཚན་བར་གྱི་བར་ཐག",
|
||||
"Override Book Layout": "པར་ངོས་ཀྱི་བཀོད་སྒྲིག་བརྗེ་བ།",
|
||||
"Untitled Group": "མགོ་མེད་སྡེ་ཚན།",
|
||||
"Group Books": "དཔེ་དེབ་སྡེ་ཚན་དུ་བསྡུ་བ།",
|
||||
"Remove From Group": "སྡེ་ཚན་ནས་ཕྱིར་འཐེན།",
|
||||
"Create New Group": "སྡེ་ཚན་གསར་པ་བཟོ་བ།",
|
||||
"Deselect Book": "དཔེ་དེབ་འདེམས་པ་འདོར་བ།",
|
||||
"Download Book": "དཔེ་དེབ་ཕབ་ལེན།",
|
||||
"Deselect Group": "སྡེ་ཚན་འདེམས་པ་འདོར་བ།",
|
||||
"Select Group": "སྡེ་ཚན་འདེམས་པ།",
|
||||
"Keep Screen Awake": "བརྡ་ཁྱབ་ངོས་རྟག་ཏུ་འོད་ཟེར་འཕྲོ་བ།",
|
||||
"Email address": "གློག་རྡུལ་ཡིག་ཟམ་གྱི་ཁ་བྱང་།",
|
||||
"Your Password": "ཁྱེད་ཀྱི་གསང་ཨང་།",
|
||||
"Your email address": "ཁྱེད་ཀྱི་གློག་རྡུལ་ཡིག་ཟམ་གྱི་ཁ་བྱང་།",
|
||||
"Your password": "ཁྱེད་ཀྱི་གསང་ཨང་།",
|
||||
"Sign in": "ནང་བསྐྱོད།",
|
||||
"Signing in...": "ནང་བསྐྱོད་བྱེད་བཞིན་པ།...",
|
||||
"Sign in with {{provider}}": "{{provider}} བེད་སྤྱད་ནས་ནང་བསྐྱོད།",
|
||||
"Already have an account? Sign in": "གོ་མིང་ཡོད་ན་ནང་བསྐྱོད་བྱོས།",
|
||||
"Create a Password": "གསང་ཨང་བཟོ་བ།",
|
||||
"Sign up": "ཐོ་འགོད།",
|
||||
"Signing up...": "ཐོ་འགོད་བྱེད་བཞིན་པ།...",
|
||||
"Don't have an account? Sign up": "གོ་མིང་མེད་ན་ཐོ་འགོད་བྱོས།",
|
||||
"Check your email for the confirmation link": "ཁྱེད་ཀྱི་གློག་རྡུལ་ཡིག་ཟམ་ནས་གཏན་འཁེལ་གྱི་སྦྲེལ་ཐག་ལ་བལྟ་དགོས།",
|
||||
"Signing in ...": "ནང་བསྐྱོད་བྱེད་བཞིན་པ།...",
|
||||
"Send a magic link email": "བྱ་བྱེད་སྦྲེལ་ཐག་གི་གློག་རྡུལ་ཡིག་ཟམ་སྤྲད་པ།",
|
||||
"Check your email for the magic link": "ཁྱེད་ཀྱི་གློག་རྡུལ་ཡིག་ཟམ་ནས་བྱ་བྱེད་སྦྲེལ་ཐག་ལ་བལྟ་དགོས།",
|
||||
"Send reset password instructions": "གསང་ཨང་བསྐྱར་སྒྲིག་གི་འགྲེལ་བཤད་སྤྲད་པ།",
|
||||
"Sending reset instructions ...": "གསང་ཨང་བསྐྱར་སྒྲིག་གི་འགྲེལ་བཤད་སྤྲད་བཞིན་པ།...",
|
||||
"Forgot your password?": "གསང་ཨང་བརྗེད་སོང་ངམ།",
|
||||
"Check your email for the password reset link": "ཁྱེད་ཀྱི་གློག་རྡུལ་ཡིག་ཟམ་ནས་གསང་ཨང་བསྐྱར་སྒྲིག་གི་སྦྲེལ་ཐག་ལ་བལྟ་དགོས།",
|
||||
"New Password": "གསང་ཨང་གསར་པ།",
|
||||
"Your new password": "ཁྱེད་ཀྱི་གསང་ཨང་གསར་པ།",
|
||||
"Update password": "གསང་ཨང་བསྐྱར་བརྗེ།",
|
||||
"Updating password ...": "གསང་ཨང་བསྐྱར་བརྗེ་བཞིན་པ།...",
|
||||
"Your password has been updated": "ཁྱེད་ཀྱི་གསང་ཨང་བསྐྱར་བརྗེ་ཟིན།",
|
||||
"Phone number": "ཁ་པར་ཨང་གྲངས།",
|
||||
"Your phone number": "ཁྱེད་ཀྱི་ཁ་པར་ཨང་གྲངས།",
|
||||
"Token": "བཀོལ་ཐོགས།",
|
||||
"Your OTP token": "ཁྱེད་ཀྱི་ OTP བཀོལ་ཐོགས།",
|
||||
"Verify token": "བཀོལ་ཐོགས་བདེན་སྦྱོང་།",
|
||||
"Sign in with Google": "Google བེད་སྤྱད་ནས་ནང་བསྐྱོད།",
|
||||
"Sign in with Apple": "Apple བེད་སྤྱད་ནས་ནང་བསྐྱོད།",
|
||||
"Sign in with GitHub": "GitHub བེད་སྤྱད་ནས་ནང་བསྐྱོད།",
|
||||
"Account": "རྩིས་ཐོ།",
|
||||
"Failed to delete user. Please try again later.": "སྤྱོད་མཁན་བསུབ་མ་ཐུབ། ཅུང་ཙམ་སྒུག་ནས་ཡང་བསྐྱར་ཚོད་ལྟ་བྱེད་རོགས།",
|
||||
"Community Support": "སྡེ་ཁུལ་གྱི་རྒྱབ་སྐྱོར།",
|
||||
"Priority Support": "གཙོ་རིམ་གྱི་རྒྱབ་སྐྱོར།",
|
||||
"Loading profile...": "མི་སྒེར་གྱི་ཆ་འཕྲིན་འགུལ་སྐྱོང་བྱེད་བཞིན་པ།...",
|
||||
"Delete Account": "རྩིས་ཐོ་བསུབ་པ།",
|
||||
"Delete Your Account?": "ཁྱེད་ཀྱི་རྩིས་ཐོ་བསུབ་དགོས་སམ།",
|
||||
"This action cannot be undone. All your data in the cloud will be permanently deleted.": "བྱ་སྤྱོད་འདི་ཕྱིར་འཐེན་བྱེད་ཐུབ་མེད། ཁྱེད་ཀྱིས་སྤྲི་དོན་ཐོག་གི་གཞི་གྲངས་ཡོད་རྒུ་རྟག་ཏུ་བསུབ་པར་འགྱུར།",
|
||||
"Delete Permanently": "རྟག་ཏུ་བསུབ་པ།",
|
||||
"RTL Direction": "གཡས་ནས་གཡོན་དུ།",
|
||||
"Maximum Column Height": "གྲལ་ཐིག་མཐོ་ཤོས།",
|
||||
"Maximum Column Width": "གྲལ་ཐིག་རྒྱ་ཆེ་ཤོས།",
|
||||
"Continuous Scroll": "རྒྱུན་མཐུད་འཁོར་རྒྱུག",
|
||||
"Fullscreen": "བརྡ་ཁྱབ་ངོས་ཆ་ཚང་།",
|
||||
"No supported files found. Supported formats: {{formats}}": "རྒྱབ་སྐྱོར་བྱེད་པའི་ཡིག་ཆ་མ་རྙེད། རྒྱབ་སྐྱོར་བྱེད་པའི་རྣམ་པ། {{formats}}",
|
||||
"Drop to Import Books": "འདྲུད་འཇོག་བྱས་ནས་དཔེ་དེབ་ནང་འདྲེན།",
|
||||
"Custom": "རང་འགུལ་གྱིས་བཟོ་བ།",
|
||||
"Custom Theme": "རང་འགུལ་གྱི་བརྗོད་བྱ།",
|
||||
"Theme Name": "བརྗོད་བྱའི་མིང་།",
|
||||
"Text Color": "ཡི་གེའི་ཚོན་མདོག",
|
||||
"Background Color": "རྒྱབ་ལྗོངས་ཀྱི་ཚོན་མདོག",
|
||||
"Preview": "སྔོན་ལྟ།",
|
||||
"Contrast": "བསྡུར་བ།",
|
||||
"Sunset": "ཉི་མ་ནུབ་པ།",
|
||||
"Double Border": "ཤོག་ལྷེ་མངོན་པ།",
|
||||
"Border Color": "ཤོག་ལྷེའི་ཚོན་མདོག",
|
||||
"Border Frame": "ཤོག་ངོས་ཀྱི་ཤོག་ལྷེ།",
|
||||
"Show Header": "ཤོག་ངོས་ཀྱི་མགོ་མངོན་པ།",
|
||||
"Show Footer": "ཤོག་ངོས་ཀྱི་མཇུག་མངོན་པ།",
|
||||
"Small": "ཆུང་བ།",
|
||||
"Large": "ཆེ་བ།",
|
||||
"Auto": "རང་འགུལ།",
|
||||
"Language": "སྐད་ཡིག",
|
||||
"No annotations to export": "ཕྱིར་འདོན་བྱ་རྒྱུའི་ཟིན་བྲིས་མེད།",
|
||||
"Author": "རྩོམ་པ་པོ།",
|
||||
"Exported from Readest": "Readest ནས་ཕྱིར་འདོན་བྱས་པ།",
|
||||
"Highlights & Annotations": "འོག་ཐིག་དང་ཟིན་བྲིས།",
|
||||
"Note": "ཟིན་བྲིས།",
|
||||
"Copied to clipboard": "འདྲ་བཤུས་ལྡེ་མིག་ཏུ་བྱས་ཟིན།",
|
||||
"Export Annotations": "ཟིན་བྲིས་ཕྱིར་འདོན།",
|
||||
"Auto Import on File Open": "ཡིག་ཆ་ཁ་ཕྱེ་སྐབས་རང་འགུལ་གྱིས་ནང་འདྲེན།",
|
||||
"LXGW WenKai GB Screen": "ཤ་ཝུ་ཝུན་ཁའེ།",
|
||||
"LXGW WenKai TC": "ཤ་ཝུ་ཝུན་ཁའེ།",
|
||||
"No chapters detected.": "ལེའུ་བརྟག་དཔྱད་བྱུང་མེད།",
|
||||
"Failed to parse the EPUB file.": "EPUB ཡིག་ཆ་འགྲེལ་བཤད་བྱེད་ཐུབ་མེད།",
|
||||
"This book format is not supported.": "དཔེ་དེབ་ཀྱི་རྣམ་པ་འདི་རྒྱབ་སྐྱོར་མི་བྱེད།",
|
||||
"Unable to fetch the translation. Please log in first and try again.": "སྒྱུར་བཅོས་ལེན་ཐུབ་མེད། ནང་བསྐྱོད་བྱས་རྗེས་ཡང་བསྐྱར་ཚོད་ལྟ་བྱེད་རོགས།",
|
||||
"Group": "སྡེ་ཚན།",
|
||||
"Always on Top": "སྒེའུ་ཁུང་གནས་སུ་བཀོད་པ།",
|
||||
"No Timeout": "དུས་ཚོད་བཀག་པ།",
|
||||
"{{value}} minute": "{{value}}སྐར་མ།",
|
||||
"{{value}} minutes": "{{value}}སྐར་མ།",
|
||||
"{{value}} hour": "{{value}}ཆུ་ཚོད།",
|
||||
"{{value}} hours": "{{value}}ཆུ་ཚོད།",
|
||||
"CJK Font": "རྒྱ་ཡིག་གི་ཡིག་གཟུགས།",
|
||||
"Clear Search": "འཚོལ་བཤེར་བསལ་བ།",
|
||||
"Header & Footer": "ཤོག་ངོས་ཀྱི་མགོ་དང་མཇུག",
|
||||
"Apply also in Scrolled Mode": "འཁོར་རྒྱུག་རྣམ་པར་སྤྱོད་པ།",
|
||||
"A new version of Readest is available!": "Readest ཐོན་རིམ་གསར་པ་བསྐྱར་བརྗེ་བྱ་རྒྱུ་ཡོད།",
|
||||
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} བསྐྱར་བརྗེ་བྱ་རྒྱུ་ཡོད།(བསྒྲིགས་ཟིན་པའི་ཐོན་རིམ {{currentVersion}})",
|
||||
"Download and install now?": "ད་ལྟ་ཕབ་ལེན་དང་བསྒྲིགས་དགོས་སམ།",
|
||||
"Downloading {{downloaded}} of {{contentLength}}": "ཕབ་ལེན་བྱེད་བཞིན་པ། {{downloaded}} / {{contentLength}}",
|
||||
"Download finished": "ཕབ་ལེན་བྱས་ཚར།",
|
||||
"DOWNLOAD & INSTALL": "ཕབ་ལེན་དང་བསྒྲིགས་པ།",
|
||||
"Changelog": "བསྐྱར་བརྗེའི་ཟིན་ཐོ།",
|
||||
"Software Update": "མཉེན་ཆས་བསྐྱར་བརྗེ།",
|
||||
"Title": "དཔེ་དེབ་ཀྱི་མིང་།",
|
||||
"Date Read": "ཀློག་པའི་ཚེས་གྲངས།",
|
||||
"Date Added": "ཁ་སྣོན་བྱས་པའི་ཚེས་གྲངས།",
|
||||
"Format": "རྣམ་པ།",
|
||||
"Ascending": "འཕར་རིམ།",
|
||||
"Descending": "འཕྲི་རིམ།",
|
||||
"Sort by...": "རིམ་སྒྲིག་བྱེད་སྟངས།...",
|
||||
"Added": "ཁ་སྣོན་བྱས་པའི་ཚེས་གྲངས།",
|
||||
"GuanKiapTsingKhai-T": "ཡོན་ཞ་ཀྲིང་ཁའེ་-T",
|
||||
"Description": "ངོ་སྤྲོད་བསྡུས་དོན།",
|
||||
"No description available": "ད་རེས་ངོ་སྤྲོད་བསྡུས་དོན་མེད།",
|
||||
"List": "ཐོ་རེའུ།",
|
||||
"Grid": "དྲྭ་རྡལ།",
|
||||
"(from 'As You Like It', Act II)": "《ཐམས་ཅད་དགའ་བ》ནས་དྲངས་པ། ལེའུ་གཉིས་པ།",
|
||||
"Link Color": "སྦྲེལ་ཐག་གི་ཚོན་མདོག",
|
||||
"Volume Keys for Page Flip": "སྒྲ་ཚད་ལྡེ་མིག་གིས་ཤོག་ལྷེ་བསྒྱུར་བ།",
|
||||
"Clicks for Page Flip": "མནན་ནས་ཤོག་ལྷེ་བསྒྱུར་བ།",
|
||||
"Swap Clicks Area": "མནན་སའི་ཁོངས་སུ་བརྗེ་བ།",
|
||||
"Screen": "བརྡ་ཁྱབ་ངོས།",
|
||||
"Orientation": "བརྡ་ཁྱབ་ངོས་ཀྱི་ཁ་ཕྱོགས།",
|
||||
"Portrait": "ཀྲིང་ངོས།",
|
||||
"Landscape": "འཕྲེད་ངོས།",
|
||||
"Open Last Book on Start": "འགོ་སློང་སྐབས་སྔོན་མར་ཀློག་པའི་དཔེ་དེབ་ཁ་ཕྱེ་བ།",
|
||||
"Checking for updates...": "བསྐྱར་བརྗེ་བརྟག་དཔྱད་བྱེད་བཞིན་པ།...",
|
||||
"Error checking for updates": "བསྐྱར་བརྗེ་བརྟག་དཔྱད་བྱེད་སྐབས་ནོར་འཁྲུལ་བྱུང་།",
|
||||
"Details": "ཞིབ་ཕྲ།",
|
||||
"File Size": "ཡིག་ཆའི་ཆེ་ཆུང་།",
|
||||
"Auto Detect": "རང་འགུལ་གྱིས་བརྟག་དཔྱད།",
|
||||
"Next Section": "ལེའུ་རྗེས་མ།",
|
||||
"Previous Section": "ལེའུ་སྔོན་མ།",
|
||||
"Next Page": "ཤོག་ལྷེ་རྗེས་མ།",
|
||||
"Previous Page": "ཤོག་ལྷེ་སྔོན་མ།",
|
||||
"Are you sure to delete {{count}} selected book(s)?_other": "ཁྱེད་ཀྱིས་འདེམས་ཟིན་པའི་དཔེ་དེབ་ {{count}} བསུབ་དགོས་སམ།",
|
||||
"Are you sure to delete the selected book?": "ཁྱེད་ཀྱིས་འདེམས་ཟིན་པའི་དཔེ་དེབ་བསུབ་དགོས་སམ།",
|
||||
"Deselect": "འདེམས་པ་འདོར་བ།",
|
||||
"Select All": "ཚང་མ་འདེམས་པ།",
|
||||
"No translation available.": "ད་རེས་སྤྱོད་རྒྱུའི་སྒྱུར་བཅོས་མེད།",
|
||||
"Translated by {{provider}}.": "{{provider}} ནས་སྒྱུར་བཅོས་བྱས་པ།",
|
||||
"DeepL": "DeepL",
|
||||
"Google Translate": "Google Translate",
|
||||
"Azure Translator": "Azure Translator",
|
||||
"Invert Image In Dark Mode": "མདོག་ནག་པོའི་བརྗོད་བྱའི་འོག་ཏུ་པར་རིས་ཀྱི་ཚོན་མདོག་ལྡོག་པ།",
|
||||
"Help improve Readest": "Readest ལེགས་བཅོས་ལ་རོགས་རམ་བྱེད་པ།",
|
||||
"Sharing anonymized statistics": "མིང་མེད་ཀྱི་བསྡོམས་རྩིས་གཞི་གྲངས་མཉམ་སྤྱོད།",
|
||||
"Interface Language": "མཐུད་ངོས་ཀྱི་སྐད་ཡིག",
|
||||
"Translation": "སྒྱུར་བཅོས།",
|
||||
"Enable Translation": "སྒྱུར་བཅོས་སྤྱོད་པ།",
|
||||
"Translation Service": "སྒྱུར་བཅོས་ཞབས་ཞུ།",
|
||||
"Translate To": "ནང་དུ་སྒྱུར་བ།",
|
||||
"Disable Translation": "སྒྱུར་བཅོས་སྒོ་བརྒྱབ།",
|
||||
"Scroll": "འཁོར་རྒྱུག",
|
||||
"Overlap Pixels": "ལྷན་པའི་པིག་སེལ།",
|
||||
"Click": "མནན་པ།",
|
||||
"System Language": "མ་ལག་གི་སྐད་ཡིག",
|
||||
"Security": "བདེ་འཇགས།",
|
||||
"Allow JavaScript": "JavaScript ཆོག་མཆན།",
|
||||
"Enable only if you trust the file.": "ཁྱེད་ཀྱིས་ཡིག་ཆ་འདིར་ཡིད་ཆེས་བྱེད་དུས་ཙམ་སྤྱོད་རྒྱུའི་གྲོས་འགལ།",
|
||||
"Sort TOC by Page": "ཤོག་ཨང་ལྟར་དཀར་ཆག་རིམ་སྒྲིག་བྱེད་པ།",
|
||||
"Search in {{count}} Book(s)..._other": "དཔེ་དེབ་ {{count}} ནང་འཚོལ་བཤེར་བྱེད་པ།...",
|
||||
"No notes match your search": "མཐུན་པའི་ཟིན་བྲིས་མ་རྙེད།",
|
||||
"Search notes and excerpts...": "ཟིན་བྲིས་འཚོལ་བཤེར།...",
|
||||
"Sign in to Sync": "ནང་བསྐྱོད་བྱས་རྗེས་སྐད་མཉམ་བྱེད་པ།",
|
||||
"Synced at {{time}}": "སྐད་མཉམ་བྱས་པའི་དུས་ཚོད། {{time}}",
|
||||
"Never synced": "ནམ་ཡང་སྐད་མཉམ་བྱས་མ་མྱོང་།",
|
||||
"Show Remaining Time": "ལྷག་མའི་དུས་ཚོད་མངོན་པ།",
|
||||
"{{time}} min left in chapter": "ལེའུ་འདིར་ད་དུང་ {{time}} སྐར་མ་ལྷག་ཡོད།",
|
||||
"Override Book Color": "དཔེ་དེབ་ཀྱི་ཚོན་མདོག་བརྗེ་བ།",
|
||||
"Login Required": "ནང་བསྐྱོད་བྱ་དགོས།",
|
||||
"Quota Exceeded": "ཚད་འཇལ་མི་འདང་།",
|
||||
"{{percentage}}% of Daily Translation Characters Used.": "ཉིན་རེའི་སྒྱུར་བཅོས་ཀྱི་ཡི་གེའི་ {{percentage}}% སྤྱོད་ཟིན།",
|
||||
"Translation Characters": "སྒྱུར་བཅོས་ཀྱི་ཡི་གེའི་གྲངས་ཀ།",
|
||||
"{{engine}}: {{count}} voices_other": "{{engine}}: {{count}} སྒྲ་རྣམ་པ།",
|
||||
"Something went wrong. Don't worry, our team has been notified and we're working on a fix.": "ཅུང་ཙམ་གྱི་གནད་དོན་བྱུང་བ་རེད། སེམས་ཁྲལ་མ་བྱེད། ང་ཚོའི་ཚོགས་པས་བརྡ་ཐོ་དང་ལེན་བྱས་ཟིན་པ་དང་ད་ལྟ་ཞིག་གི་ནང་བཅོས་བྱེད་བཞིན་པ་རེད།",
|
||||
"Error Details:": "ནོར་འཁྲུལ་གྱི་ཞིབ་ཕྲ།",
|
||||
"Try Again": "ཡང་བསྐྱར་ཚོད་ལྟ།",
|
||||
"Need help?": "རོགས་རམ་དགོས་སམ།",
|
||||
"Contact Support": "ཞབས་འདེགས་པར་འབྲེལ་བ།",
|
||||
"Code Highlighting": "ཨང་རྟགས་གསལ་བཀྲོལ།",
|
||||
"Enable Highlighting": "གསལ་བཀྲོལ་སྤྱོད་པ།",
|
||||
"Code Language": "ཨང་རྟགས་ཀྱི་སྐད་ཡིག",
|
||||
"Top Margin (px)": "སྟེང་ངོས་ཀྱི་བར་ཐག",
|
||||
"Bottom Margin (px)": "འོག་ངོས་ཀྱི་བར་ཐག",
|
||||
"Right Margin (px)": "གཡས་ངོས་ཀྱི་བར་ཐག",
|
||||
"Left Margin (px)": "གཡོན་ངོས་ཀྱི་བར་ཐག",
|
||||
"Column Gap (%)": "གྲལ་ཐིག་བར་གྱི་བར་ཐག",
|
||||
"Always Show Status Bar": "རྟག་ཏུ་གནས་ཚུལ་གྱི་གྲལ་ཐིག་མངོན་པ།",
|
||||
"Translation Not Available": "སྒྱུར་བཅོས་སྤྱོད་ཐུབ་མེད།",
|
||||
"Custom Content CSS": "རང་འགུལ་གྱི་ནང་དོན་ CSS",
|
||||
"Enter CSS for book content styling...": "དཔེ་དེབ་ཀྱི་ནང་དོན་གྱི་རྣམ་པའི་ CSS ནང་འཇུག་བྱོས།...",
|
||||
"Custom Reader UI CSS": "རང་འགུལ་གྱི་མཐུད་ངོས་ CSS",
|
||||
"Enter CSS for reader interface styling...": "ཀློག་ཆས་ཀྱི་མཐུད་ངོས་རྣམ་པའི་ CSS ནང་འཇུག་བྱོས།...",
|
||||
"Crop": "བཏོག་པ།",
|
||||
"Book Covers": "དཔེ་དེབ་ཀྱི་སྟོང་ངོས།",
|
||||
"Fit": "མཐུན་སྒྲིག",
|
||||
"Reset {{settings}}": "{{settings}} བསྐྱར་སྒྲིག",
|
||||
"Reset Settings": "སྒྲིག་བཀོད་བསྐྱར་སྒྲིག",
|
||||
"{{count}} pages left in chapter_other": "ལེའུ་འདིར་ད་དུང་ {{count}} ཤོག་ལྷེ་ལྷག་ཡོད།",
|
||||
"Show Remaining Pages": "ལྷག་མའི་ཤོག་ལྷེའི་གྲངས་ཀ་མངོན་པ།",
|
||||
"Source Han Serif CN VF": "སི་ཡོན་སུང་ཐི།",
|
||||
"Huiwen-mincho": "ཧུའེ་ཝུན་མིང་ཁྲའོ་ཐི།",
|
||||
"KingHwa_OldSong": "ཅིང་ཧྭ་ལའོ་སུང་ཐི།",
|
||||
"Manage Subscription": "མཚམས་སྦྱོར་དོ་དམ།",
|
||||
"Coming Soon": "ཉེ་བར་སྤྱོད་འགོ་འཛུགས་རྒྱུ།",
|
||||
"Upgrade to {{plan}}": "{{plan}} ལ་རིམ་སྤོར་བྱེད་པ།",
|
||||
"Upgrade to Plus or Pro": "Plus ཡང་ན་ Pro ལ་རིམ་སྤོར་བྱེད་པ།",
|
||||
"Current Plan": "ད་ལྟའི་ཐུམ་སྒྲིལ།",
|
||||
"Plan Limits": "ཐུམ་སྒྲིལ་གྱི་ཚད་བཀག",
|
||||
"Processing your payment...": "དངུལ་སྤྲོད་པར་བརྟག་དཔྱད་བྱེད་བཞིན་པ།...",
|
||||
"Please wait while we confirm your subscription.": "ཅུང་ཙམ་སྒུག་དང་། ང་ཚོས་ཁྱེད་ཀྱི་མཚམས་སྦྱོར་གཏན་འཁེལ་བྱེད་བཞིན་པ་རེད།",
|
||||
"Payment Processing": "དངུལ་སྤྲོད་པར་བརྟག་དཔྱད་བྱེད་བཞིན་པ།",
|
||||
"Your payment is being processed. This usually takes a few moments.": "ཁྱེད་ཀྱི་དངུལ་སྤྲོད་པར་བརྟག་དཔྱད་བྱེད་བཞིན་པ་ཡིན། སྤྱིར་ན་སྐར་ཆ་ཁ་ཤས་དགོས།",
|
||||
"Payment Failed": "དངུལ་སྤྲོད་མ་ཐུབ།",
|
||||
"We couldn't process your subscription. Please try again or contact support if the issue persists.": "ང་ཚོས་ཁྱེད་ཀྱི་མཚམས་སྦྱོར་བརྟག་དཔྱད་བྱེད་ཐུབ་མེད། ཡང་བསྐྱར་ཚོད་ལྟ་བྱོས། ཡང་ན་གནད་དོན་རྒྱུན་མཐུད་བྱུང་ན་རྒྱབ་སྐྱོར་ལ་འབྲེལ་བ་གནང་རོགས།",
|
||||
"Back to Profile": "མི་སྒེར་གྱི་ཆ་འཕྲིན་ལ་ཕྱིར་ལོག",
|
||||
"Subscription Successful!": "མཚམས་སྦྱོར་བྱས་ཐུབ།",
|
||||
"Thank you for your subscription! Your payment has been processed successfully.": "ཁྱེད་ཀྱི་མཚམས་སྦྱོར་ལ་ཐུགས་རྗེ་ཆེ། ཁྱེད་ཀྱི་དངུལ་སྤྲོད་ཐུབ་སོང་།",
|
||||
"Email:": "གློག་རྡུལ་ཡིག་ཟམ།",
|
||||
"Plan:": "ཐུམ་སྒྲིལ།",
|
||||
"Amount:": "དངུལ་གྲངས།",
|
||||
"Subscription ID:": "མཚམས་སྦྱོར་ ID །",
|
||||
"Go to Library": "དཔེ་མཛོད་ལ་འགྲོ།",
|
||||
"Need help? Contact our support team at support@readest.com": "རོགས་རམ་དགོས་སམ། རྒྱབ་སྐྱོར་ཚོགས་པར་འབྲེལ་བ་གནང་རོགས། support@readest.com",
|
||||
"Free Plan": "རིན་མེད་ཐུམ་སྒྲིལ།",
|
||||
"month": "ཟླ་བ།",
|
||||
"AI Translations (per day)": "AI སྒྱུར་བཅོས།(ཉིན་རེར།)",
|
||||
"Plus Plan": "Plus ཐུམ་སྒྲིལ།",
|
||||
"Includes All Free Plan Benefits": "རིན་མེད་ཐུམ་སྒྲིལ་གྱི་བྱེད་ནུས་ཚང་མ་ཚུད་ཡོད།",
|
||||
"Pro Plan": "Pro ཐུམ་སྒྲིལ།",
|
||||
"More AI Translations": "AI སྒྱུར་བཅོས་དེ་བས་མང་བ།",
|
||||
"Complete Your Subscription": "ཁྱེད་ཀྱི་མཚམས་སྦྱོར་ལེགས་གྲུབ།",
|
||||
"{{percentage}}% of Cloud Sync Space Used.": "སྤྲི་དོན་སྐད་མཉམ་གྱི་བར་སྟོང་གི་ {{percentage}}% སྤྱོད་ཟིན།",
|
||||
"Cloud Sync Storage": "སྤྲི་དོན་སྐད་མཉམ་གྱི་བར་སྟོང་།",
|
||||
"Disable": "སྒོ་བརྒྱབ།",
|
||||
"Enable": "སྤྱོད་པ།",
|
||||
"Upgrade to Readest Premium": "Readest Premium ལ་རིམ་སྤོར་བྱེད་པ།",
|
||||
"Show Source Text": "མགོ་ཡིག་མངོན་པ།",
|
||||
"Cross-Platform Sync": "ལས་སྟེགས་བརྒལ་བའི་སྐད་མཉམ།",
|
||||
"Seamlessly sync your library, progress, highlights, and notes across all your devices—never lose your place again.": "སྒྲིག་ཆས་ཡོད་རྒུའི་བར་དུ་ཁྱེད་ཀྱི་དཔེ་མཛོད། ཡར་འཕེལ། མཚན་སྟོན་དང་ཟིན་བྲིས་བཅས་སྐད་མཉམ་བྱེད་པ།——ཀློག་པའི་གནས་ས་ནམ་ཡང་བོར་མི་སྲིད།",
|
||||
"Customizable Reading": "མི་སྒེར་གྱི་ཀློག་ཚུལ།",
|
||||
"Personalize every detail with adjustable fonts, layouts, themes, and advanced display settings for the perfect reading experience.": "འགྱུར་བཅོས་བྱེད་ཐུབ་པའི་ཡིག་གཟུགས། བཀོད་སྒྲིག བརྗོད་བྱ་དང་མཐོ་རིམ་གྱི་མངོན་པའི་སྒྲིག་བཀོད་བཅས་བརྒྱུད་ནས་ཞིབ་ཕྲ་རེ་རེ་མི་སྒེར་གྱི་རང་བཞིན་ལྡན་པར་བྱས་ཏེ། ངོ་མཚར་ཆེ་བའི་ཀློག་ཚུལ་ཞིག་ཐོབ།",
|
||||
"AI Read Aloud": "AI སྒྲ་ཀློག",
|
||||
"Enjoy hands-free reading with natural-sounding AI voices that bring your books to life.": "ལག་པ་གཉིས་ཐར་བའི་ཀློག་ཚུལ་ལོངས་སུ་སྤྱོད། རང་བཞིན་གྱི་ AI སྒྲ་སྐད་བེད་སྤྱད་ནས་དཔེ་དེབ་སླར་གསོ་བྱེད།",
|
||||
"AI Translations": "AI སྒྱུར་བཅོས།",
|
||||
"Translate any text instantly with the power of Google, Azure, or DeepL—understand content in any language.": "Google དང་ Azure ཡང་ན་ DeepL བཅས་ཀྱི་ནུས་པ་ཆེ་བའི་བྱེད་ནུས་བརྒྱུད་ནས་སྐད་ཡིག་གང་རུང་གི་ནང་དོན་དུས་ཐོག་ཏུ་སྒྱུར་བཅོས་བྱེད།",
|
||||
"Connect with fellow readers and get help fast in our friendly community channels.": "ཀློག་པ་པ་གཞན་དང་མཉམ་འབྲེལ་བྱས་ཏེ། ང་ཚོའི་མཛའ་བརྩེའི་སྡེ་ཁུལ་གྱི་ཐོག་མཐུད་ནང་མྱུར་དུ་རོགས་རམ་ཐོབ།",
|
||||
"Unlimited AI Read Aloud Hours": "AI སྒྲ་ཀློག་གི་དུས་ཚོད་ཚད་མེད།",
|
||||
"Listen without limits—convert as much text as you like into immersive audio.": "ཡི་གེ་གང་འདོད་སྒྲ་སྐད་དུ་བསྒྱུར་བ།",
|
||||
"Unlock enhanced translation capabilities with more daily usage and advanced options.": "སྒྱུར་བཅོས་ཀྱི་བྱེད་ནུས་ཇེ་མཐོར་གཏོང་བ། སྒྱུར་བཅོས་ཀྱི་ཚད་འཇལ་དེ་བས་མང་བ། མཐོ་རིམ་གྱི་འདེམས་གཞི།",
|
||||
"DeepL Pro Access": "DeepL Pro ཀྱི་སྤྱོད་དབང་།",
|
||||
"Enjoy faster responses and dedicated assistance whenever you need help.": "དུས་ཐོག་ཏུ་ལན་འདེབས་མགྱོགས་པོ་དང་ཆེད་སྤྱོད་ཀྱི་རོགས་རམ་ལོངས་སུ་སྤྱོད།",
|
||||
"Daily translation quota reached. Upgrade your plan to continue using AI translations.": "ཉིན་རེའི་སྒྱུར་བཅོས་ཀྱི་ཚད་འཇལ་ལ་སླེབས་འདུག ཐུམ་སྒྲིལ་རིམ་སྤོར་བྱས་ནས་ AI སྒྱུར་བཅོས་མུ་མཐུད་དུ་སྤྱོད།",
|
||||
"Includes All Plus Plan Benefits": "Plus ཐུམ་སྒྲིལ་གྱི་བྱེད་ནུས་ཚང་མ་ཚུད་ཡོད།",
|
||||
"Early Feature Access": "བྱེད་ནུས་གསར་པ་སྔོན་ལ་ཉམས་སུ་མྱོང་བ།",
|
||||
"Be the first to explore new features, updates, and innovations before anyone else.": "བྱེད་ནུས་གསར་པ། བསྐྱར་བརྗེ་དང་གསར་གཏོད་བཅས་སྔོན་ལ་ཉམས་སུ་མྱོང་བ།",
|
||||
"Advanced AI Tools": "མཐོ་རིམ་གྱི་ AI ལག་ཆ།",
|
||||
"Harness powerful AI tools for smarter reading, translation, and content discovery.": "ནུས་པ་ཆེ་བའི་ AI ལག་ཆ་བེད་སྤྱད་ནས་ཤེས་ལྡན་གྱི་ཀློག་ཚུལ། སྒྱུར་བཅོས་དང་ནང་དོན་རྟོགས་པ་བཅས་མངོན་འགྱུར་བྱེད།",
|
||||
"Translate up to 100,000 characters daily with the most accurate translation engine available.": "ཆེས་གནད་ལ་ཁེལ་བའི་སྒྱུར་བཅོས་འཕྲུལ་ཆས་བེད་སྤྱད་ནས་ཉིན་རེར་ཡིག་འབྲུ་ཁྲི་ཚོ 10 སྒྱུར་བཅོས་བྱེད་ཐུབ།",
|
||||
"Translate up to 500,000 characters daily with the most accurate translation engine available.": "ཆེས་གནད་ལ་ཁེལ་བའི་སྒྱུར་བཅོས་འཕྲུལ་ཆས་བེད་སྤྱད་ནས་ཉིན་རེར་ཡིག་འབྲུ་ཁྲི་ཚོ 50 སྒྱུར་བཅོས་བྱེད་ཐུབ།",
|
||||
"Securely store and access your entire reading collection with up to 5 GB of cloud storage.": "བདེ་འཇགས་ཀྱི་སྤྲི་དོན་ཉར་ཚགས་ 5GB བར་ཁྱབ་ཏུ་བེད་སྤྱད་ནས་ཁྱེད་ཀྱི་ཀློག་ཚུལ་ཡོད་རྒུ་ཉར་ཚགས་དང་སྤྱོད་པ།",
|
||||
"Securely store and access your entire reading collection with up to 20 GB of cloud storage.": "བདེ་འཇགས་ཀྱི་སྤྲི་དོན་ཉར་ཚགས་ 20GB བར་ཁྱབ་ཏུ་བེད་སྤྱད་ནས་ཁྱེད་ཀྱི་ཀློག་ཚུལ་ཡོད་རྒུ་ཉར་ཚགས་དང་སྤྱོད་པ།",
|
||||
"Deleted cloud backup of the book: {{title}}": "དཔེ་དེབ་ཀྱི་སྤྲི་དོན་གྱི་གསོག་ཉར་བསུབ་ཟིན། {{title}}",
|
||||
"Are you sure to delete the cloud backup of the selected book?": "ཁྱེད་ཀྱིས་འདེམས་ཟིན་པའི་དཔེ་དེབ་ཀྱི་སྤྲི་དོན་གྱི་གསོག་ཉར་བསུབ་དགོས་སམ།",
|
||||
"What's New in Readest": "Readest ཀྱི་བྱེད་ནུས་གསར་པ།",
|
||||
"Enter book title": "དཔེ་དེབ་ཀྱི་མིང་ནང་འཇུག་བྱོས།",
|
||||
"Subtitle": "ལེའུ་གཉིས་པའི་མགོ་བྱང་།",
|
||||
"Enter book subtitle": "དཔེ་དེབ་ཀྱི་ལེའུ་གཉིས་པའི་མགོ་བྱང་ནང་འཇུག་བྱོས།",
|
||||
"Enter author name": "རྩོམ་པ་པོའི་མིང་ནང་འཇུག་བྱོས།",
|
||||
"Series": "རབས་རིམ།",
|
||||
"Enter series name": "རབས་རིམ་གྱི་མིང་ནང་འཇུག་བྱོས།",
|
||||
"Series Index": "རབས་རིམ་གྱི་ཨང་གྲངས།",
|
||||
"Enter series index": "རབས་རིམ་གྱི་ཨང་གྲངས་ནང་འཇུག་བྱོས།",
|
||||
"Total in Series": "རབས་རིམ་གྱི་བསྡོམས་གྲངས།",
|
||||
"Enter total books in series": "རབས་རིམ་ནང་གི་དཔེ་དེབ་བསྡོམས་གྲངས་ནང་འཇུག་བྱོས།",
|
||||
"Enter publisher": "དཔེ་སྐྲུན་ཁང་ནང་འཇུག་བྱོས།",
|
||||
"Publication Date": "དཔེ་སྐྲུན་བྱས་པའི་ཚེས་གྲངས།",
|
||||
"Identifier": "ངོས་འཛིན་རྟགས།",
|
||||
"Enter book description": "དཔེ་དེབ་ཀྱི་ངོ་སྤྲོད་ནང་འཇུག་བྱོས།",
|
||||
"Change cover image": "སྟོང་ངོས་ཀྱི་པར་རིས་བརྗེ་བ།",
|
||||
"Replace": "བརྗེ་བ།",
|
||||
"Unlock cover": "སྟོང་ངོས་སྒོ་ཕྱེ་བ།",
|
||||
"Lock cover": "སྟོང་ངོས་བཀག་པ།",
|
||||
"Auto-Retrieve Metadata": "རང་འགུལ་གྱིས་གཞི་གྲངས་ལེན་པ།",
|
||||
"Auto-Retrieve": "རང་འགུལ་གྱིས་ལེན་པ།",
|
||||
"Unlock all fields": "ཡིག་སྣེ་ཚང་མ་སྒོ་ཕྱེ་བ།",
|
||||
"Unlock All": "ཚང་མ་སྒོ་ཕྱེ་བ།",
|
||||
"Lock all fields": "ཡིག་སྣེ་ཚང་མ་བཀག་པ།",
|
||||
"Lock All": "ཚང་མ་བཀག་པ།",
|
||||
"Reset": "བསྐྱར་སྒྲིག",
|
||||
"Edit Metadata": "གཞི་གྲངས་རྩོམ་སྒྲིག",
|
||||
"Locked": "བཀག་ཟིན།",
|
||||
"Select Metadata Source": "གཞི་གྲངས་ཀྱི་འབྱུང་ཁུངས་འདེམས་པ།",
|
||||
"Keep manual input": "ལག་བཟོས་ནང་འཇུག་བྱས་པ་ཉར་བ།",
|
||||
"Google Books": "Google དཔེ་མཛོད།",
|
||||
"Open Library": "སྒོ་འབྱེད་དཔེ་ཁང་།",
|
||||
"Fiction, Science, History": "སྒྲུང་གཏམ། ཚན་རིག ལོ་རྒྱུས།",
|
||||
"Select Cover Image": "སྟོང་ངོས་ཀྱི་པར་རིས་འདེམས་པ།",
|
||||
"Open Book in New Window": "སྒེའུ་ཁུང་གསར་པའི་ནང་དཔེ་དེབ་ཁ་ཕྱེ་བ།",
|
||||
"Voices for {{lang}}": "{{lang}} གྱི་སྒྲ་སྐད།",
|
||||
"Yandex Translate": "Yandex སྒྱུར་བཅོས།",
|
||||
"YYYY or YYYY-MM-DD": "YYYY ཡང་ན་ YYYY-MM-DD",
|
||||
"Restore Purchase": "ཉོ་སྒྲུབ་སླར་གསོ།",
|
||||
"No purchases found to restore.": "སླར་གསོ་བྱ་རྒྱུའི་ཉོ་སྒྲུབ་མ་རྙེད།",
|
||||
"Failed to restore purchases.": "ཉོ་སྒྲུབ་སླར་གསོ་བྱས་མ་ཐུབ།",
|
||||
"Failed to manage subscription.": "མཚམས་སྦྱོར་དོ་དམ་བྱས་མ་ཐུབ།",
|
||||
"Failed to load subscription plans.": "མཚམས་སྦྱོར་འཆར་གཞི་འགུལ་སྐྱོང་བྱས་མ་ཐུབ།",
|
||||
"year": "ལོ།",
|
||||
"Failed to create checkout session": "དངུལ་སྤྲོད་ཚོགས་འདུ་བཟོ་ཐུབ་མེད།",
|
||||
"Storage": "སྤྲི་དོན་གྱི་བར་སྟོང་།",
|
||||
"Terms of Service": "ཞབས་འདེགས་ཀྱི་ཆ་རྐྱེན།",
|
||||
"Privacy Policy": "སྒེར་གསང་གི་སྲིད་ཇུས།",
|
||||
"Disable Double Click": "ཐེངས་གཉིས་མནན་པ་སྒོ་བརྒྱབ།",
|
||||
"TTS not supported for this document": "ཡིག་ཆ་འདིར་ TTS རྒྱབ་སྐྱོར་མི་བྱེད།",
|
||||
"Reset Password": "གསང་ཨང་བསྐྱར་སྒྲིག",
|
||||
"Show Reading Progress": "ཀློག་པའི་ཡར་འཕེལ་མངོན་པ།",
|
||||
"Reading Progress Style": "ཀློག་པའི་ཡར་འཕེལ་གྱི་རྣམ་པ།",
|
||||
"Page Number": "ཤོག་ཨང་།",
|
||||
"Percentage": "བརྒྱ་ཆ།",
|
||||
"Deleted local copy of the book: {{title}}": "དཔེ་དེབ་ཀྱི་ས་གནས་ཀྱི་འདྲ་བཤུས་བསུབ་ཟིན། {{title}}",
|
||||
"Failed to delete cloud backup of the book: {{title}}": "དཔེ་དེབ་ཀྱི་སྤྲི་དོན་གྱི་གསོག་ཉར་བསུབ་ཐུབ་མེད། {{title}}",
|
||||
"Failed to delete local copy of the book: {{title}}": "དཔེ་དེབ་ཀྱི་ས་གནས་ཀྱི་འདྲ་བཤུས་བསུབ་ཐུབ་མེད། {{title}}",
|
||||
"Are you sure to delete the local copy of the selected book?": "ཁྱེད་ཀྱིས་འདེམས་ཟིན་པའི་དཔེ་དེབ་ཀྱི་ས་གནས་ཀྱི་འདྲ་བཤུས་བསུབ་དགོས་སམ།",
|
||||
"Remove from Cloud & Device": "སྤྲི་དོན་དང་སྒྲིག་ཆས་ནས་ཕྱིར་འཐེན།",
|
||||
"Remove from Cloud Only": "སྤྲི་དོན་ཐོག་ནས་ཕྱིར་འཐེན།",
|
||||
"Remove from Device Only": "སྒྲིག་ཆས་ཐོག་ནས་ཕྱིར་འཐེན།",
|
||||
"Failed to connect": "འབྲེལ་བ་བྱས་མ་ཐུབ།",
|
||||
"Disconnected": "འབྲེལ་བ་བྱས་མ་ཐུབ།",
|
||||
"KOReader Sync Settings": "KOReader སྤྲི་དོན་གཞི་འདེམས་པ།",
|
||||
"Sync as {{userDisplayName}}": "ཁྱེད་ཀྱིས {{userDisplayName}} སྤྲི་དོན་གཞི་འདེམས་པ།",
|
||||
"Sync Server Connected": "སྤྲི་དོན་སང་བཞིན་འདེམས་པ།",
|
||||
"Sync Strategy": "སྤྲི་དོན་འབྱུང་ཁུངས།",
|
||||
"Ask on conflict": "འབྱུང་ཁུངས་འདེམས་པ་བྱས་ནས་བརྗེ་བ།",
|
||||
"Always use latest": "དེ་ལས་འབྱུང་ཁུངས་འདེམས་པ་བྱས་ནས་བརྗེ་བ།",
|
||||
"Send changes only": "འབྱུང་ཁུངས་འདེམས་པ་བྱས་ནས་བརྗེ་བ།",
|
||||
"Receive changes only": "འབྱུང་ཁུངས་འདེམས་པ་བྱས་ནས་བརྗེ་བ།",
|
||||
"Disabled": "བཀག་པ།",
|
||||
"Checksum Method": "ཡིག་སྣེ་འབྱུང་ཁུངས།",
|
||||
"File Content (recommended)": "ཡིག་སྣེ་འབྱུང་ཁུངས།",
|
||||
"File Name": "ཡིག་སྣེ་འབྱུང་ཁུངས།",
|
||||
"Device Name": "ཡིག་སྣེ་འབྱུང་ཁུངས།",
|
||||
"Sync Tolerance": "ཡིག་སྣེ་འབྱུང་ཁུངས།",
|
||||
"Precision: {{precision}} digits after the decimal": "ཡིག་སྣེ་འབྱུང་ཁུངས།",
|
||||
"Connect to your KOReader Sync server.": "ཁྱེད་ཀྱིས KOReader སྤྲི་དོན་སང་བཞིན་འདེམས་པ།",
|
||||
"Server URL": "སང་བཞིན་འདེམས་པའི URL",
|
||||
"Username": "ཁྱེད་ཀྱིས་འབྱུང་ཁུངས་འདེམས་པ།",
|
||||
"Your Username": "ཁྱེད་ཀྱིས་འབྱུང་ཁུངས་འདེམས་པ།",
|
||||
"Password": "གསང་ཨང་",
|
||||
"Connect": "འབྲེལ་བ་བྱས་པ།",
|
||||
"KOReader Sync": "KOReader སྤྲི་དོན་འདེམས་པ།",
|
||||
"Sync Conflict": "སྤྲི་དོན་འབྱུང་ཁུངས།",
|
||||
"Sync reading progress from \"{{deviceName}}\"?": "ཁྱེད་ཀྱིས \"{{deviceName}}\" སྤྲི་དོན་འབྱུང་ཁུངས་འདེམས་པ་བྱས་ནས་བརྗེ་བ།",
|
||||
"another device": "གཞན་ཡིག་སྣེ།",
|
||||
"Local Progress": "ཀུན་འབྱུང་ཁུངས།",
|
||||
"Remote Progress": "བརྒྱབ་འབྱུང་ཁུངས།",
|
||||
"All the world's a stage,\nAnd all the men and women merely players;\nThey have their exits and their entrances,\nAnd one man in his time plays many parts,\nHis acts being seven ages.\n\n— William Shakespeare": "འབྱུང་ཁུངས་འདེམས་པ་བྱས་ནས་བརྗེ་བ།",
|
||||
"Page {{page}} of {{total}} ({{percentage}}%)": "ཤོག་ {{page}} དང་ {{total}} ({{percentage}}%)",
|
||||
"Current position": "ད་ལྟ་བཞིན་འདེམས་པ།",
|
||||
"Approximately page {{page}} of {{total}} ({{percentage}}%)": "ད་ལྟ་བཞིན་འདེམས་པ།",
|
||||
"Approximately {{percentage}}%": "ད་ལྟ་བཞིན་འདེམས་པ།"
|
||||
}
|
||||
@@ -18,7 +18,6 @@
|
||||
"Confirm Deletion": "Löschen bestätigen",
|
||||
"Copied to notebook": "In Notizbuch kopiert",
|
||||
"Copy": "Kopieren",
|
||||
"Custom CSS": "Benutzerdefiniertes CSS",
|
||||
"Dark Mode": "Dunkelmodus",
|
||||
"Default": "Standard",
|
||||
"Default Font": "Standardschriftart",
|
||||
@@ -28,7 +27,6 @@
|
||||
"Dictionary": "Wörterbuch",
|
||||
"Download Readest": "Readest herunterladen",
|
||||
"Edit": "Bearbeiten",
|
||||
"Enter your custom CSS here...": "Geben Sie hier Ihr benutzerdefiniertes CSS ein...",
|
||||
"Excerpts": "Auszüge",
|
||||
"Failed to import book(s): {{filenames}}": "Fehler beim Importieren von Buch/Büchern: {{filenames}}",
|
||||
"Fast": "Schnell",
|
||||
@@ -48,7 +46,6 @@
|
||||
"Horizontal Direction": "Horizontale Richtung",
|
||||
"Hyphenation": "Silbentrennung",
|
||||
"Import Books": "Bücher importieren",
|
||||
"Language:": "Sprache:",
|
||||
"Layout": "Layout",
|
||||
"Light Mode": "Hellmodus",
|
||||
"Loading...": "Wird geladen...",
|
||||
@@ -69,9 +66,8 @@
|
||||
"Page": "Seite",
|
||||
"Paging Animation": "Blätter-Animation",
|
||||
"Paragraph": "Absatz",
|
||||
"Parallel Read": "Paralleles Lesen",
|
||||
"Published:": "Veröffentlicht:",
|
||||
"Publisher:": "Verlag:",
|
||||
"Published": "Veröffentlicht",
|
||||
"Publisher": "Verlag",
|
||||
"Reading Progress Synced": "Lesevorgang synchronisiert",
|
||||
"Reload Page": "Seite neu laden",
|
||||
"Reveal in File Explorer": "Im Datei-Explorer anzeigen",
|
||||
@@ -96,16 +92,15 @@
|
||||
"Slow": "Langsam",
|
||||
"Solarized": "Solarisiert",
|
||||
"Speak": "Sprechen",
|
||||
"Subjects:": "Themen:",
|
||||
"Subjects": "Themen",
|
||||
"System Fonts": "System-Schriftarten",
|
||||
"TTS not supported in this device": "TTS wird auf diesem Gerät nicht unterstützt",
|
||||
"Theme Color": "Themenfarbe",
|
||||
"Theme Mode": "Themenmodus",
|
||||
"Translate": "Übersetzen",
|
||||
"Translated Text": "Übersetzter Text",
|
||||
"Unknown": "Unbekannt",
|
||||
"Untitled": "Ohne Titel",
|
||||
"Updated:": "Aktualisiert:",
|
||||
"Updated": "Aktualisiert",
|
||||
"Version {{version}}": "Version {{version}}",
|
||||
"Vertical Direction": "Vertikale Richtung",
|
||||
"Welcome to your library. You can import your books here and read them anytime.": "Willkommen in Ihrer Bibliothek. Sie können hier Ihre Bücher importieren und jederzeit lesen.",
|
||||
@@ -114,8 +109,6 @@
|
||||
"Your Library": "Ihre Bibliothek",
|
||||
"TTS not supported for PDF": "TTS wird für PDF nicht unterstützt",
|
||||
"Override Book Font": "Buch-Schriftart überschreiben",
|
||||
"Vertical Margins (px)": "Vertikale Ränder (px)",
|
||||
"Horizontal Margins (%)": "Horizontale Ränder (%)",
|
||||
"Apply to All Books": "Auf alle Bücher anwenden",
|
||||
"Apply to This Book": "Auf dieses Buch anwenden",
|
||||
"Unable to fetch the translation. Try again later.": "Übersetzung konnte nicht abgerufen werden. Versuchen Sie es später erneut.",
|
||||
@@ -130,8 +123,6 @@
|
||||
"Failed to download book: {{title}}": "Fehler beim Herunterladen des Buches: {{title}}",
|
||||
"Upload Book": "Buch hochladen",
|
||||
"Auto Upload Books to Cloud": "Bücher automatisch hochladen",
|
||||
"{{percentage}}% of Cloud Storage Used.": "{{percentage}}% des Cloud-Speichers verwendet.",
|
||||
"Storage": "Speicher",
|
||||
"Book deleted: {{title}}": "Buch gelöscht: {{title}}",
|
||||
"Failed to delete book: {{title}}": "Fehler beim Löschen des Buches: {{title}}",
|
||||
"Check Updates on Start": "Aktualisierungen beim Start prüfen",
|
||||
@@ -163,7 +154,7 @@
|
||||
"Create a Password": "Passwort erstellen",
|
||||
"Sign up": "Registrieren",
|
||||
"Signing up...": "Registrierung...",
|
||||
"Don’t have an account? Sign up": "Kein Konto? Registrieren",
|
||||
"Don't have an account? Sign up": "Kein Konto? Registrieren",
|
||||
"Check your email for the confirmation link": "Überprüfen Sie Ihre E-Mails auf den Bestätigungslink",
|
||||
"Signing in ...": "Anmeldung ...",
|
||||
"Send a magic link email": "Magic-Link-E-Mail senden",
|
||||
@@ -187,35 +178,13 @@
|
||||
"Sign in with GitHub": "Mit GitHub anmelden",
|
||||
"Account": "Konto",
|
||||
"Failed to delete user. Please try again later.": "Benutzer konnte nicht gelöscht werden. Bitte versuchen Sie es später erneut.",
|
||||
"Unlimited Offline/Online Reading": "Unbegrenztes Offline/Online Lesen",
|
||||
"Unlimited Cloud Sync Devices": "Unbegrenzte Cloud-Synchronisierungsgeräte",
|
||||
"Essential Text-to-Speech Voices": "Grundlegende Text-zu-Sprache-Stimmen",
|
||||
"DeepL Free Access": "Kostenloser DeepL-Zugang",
|
||||
"Community Support": "Community-Support",
|
||||
"500 MB Cloud Sync Space": "500 MB Cloud-Synchronisierungsspeicher",
|
||||
"Includes All Free Tier Benefits": "Beinhaltet alle Vorteile der kostenlosen Stufe",
|
||||
"AI Summaries": "KI-Zusammenfassungen",
|
||||
"AI Translations": "KI-Übersetzungen",
|
||||
"Priority Support": "Bevorzugter Support",
|
||||
"DeepL Pro Access": "DeepL Pro-Zugang",
|
||||
"Expanded Text-to-Speech Voices": "Erweiterte Text-zu-Sprache-Stimmen",
|
||||
"2000 MB Cloud Sync Space": "2000 MB Cloud-Synchronisierungsspeicher",
|
||||
"Includes All Plus Tier Benefits": "Beinhaltet alle Vorteile der Plus-Stufe",
|
||||
"Unlimited AI Summaries": "Unbegrenzte KI-Zusammenfassungen",
|
||||
"Unlimited AI Translations": "Unbegrenzte KI-Übersetzungen",
|
||||
"Unlimited DeepL Pro Access": "Unbegrenzter DeepL Pro-Zugang",
|
||||
"Advanced AI Tools": "Fortgeschrittene KI-Tools",
|
||||
"Early Feature Access": "Früher Zugang zu neuen Funktionen",
|
||||
"10 GB Cloud Sync Space": "10 GB Cloud-Synchronisierungsspeicher",
|
||||
"Loading profile...": "Profil wird geladen...",
|
||||
"Features": "Funktionen",
|
||||
"Delete Account": "Konto löschen",
|
||||
"Delete Your Account?": "Ihr Konto löschen?",
|
||||
"This action cannot be undone. All your data in the cloud will be permanently deleted.": "Diese Aktion kann nicht rückgängig gemacht werden. Alle Ihre Daten in der Cloud werden dauerhaft gelöscht.",
|
||||
"Delete Permanently": "Dauerhaft löschen",
|
||||
"Free Tier": "Kostenlose Stufe",
|
||||
"Plus Tier": "Plus-Stufe",
|
||||
"Pro Tier": "Pro-Stufe",
|
||||
"RTL Direction": "RTL-Richtung",
|
||||
"Maximum Column Height": "Maximale Spaltenhöhe",
|
||||
"Maximum Column Width": "Maximale Spaltenbreite",
|
||||
@@ -223,7 +192,7 @@
|
||||
"Fullscreen": "Vollbild",
|
||||
"No supported files found. Supported formats: {{formats}}": "Keine unterstützten Dateien gefunden. Unterstützte Formate: {{formats}}",
|
||||
"Drop to Import Books": "Zum Importieren von Büchern ablegen",
|
||||
"Custom": "Benutzerdefiniert",
|
||||
"Custom": "Eigen",
|
||||
"All the world's a stage,\nAnd all the men and women merely players;\nThey have their exits and their entrances,\nAnd one man in his time plays many parts,\nHis acts being seven ages.\n\n— William Shakespeare": "Die ganze Welt ist eine Bühne,\nUnd alle Männer und Frauen sind bloß Spieler;\nSie haben ihre Abgänge und ihre Auftritte,\nUnd ein Mann spielt in seinem Leben viele Rollen,\nSeine Taten sind sieben Zeitalter.\n\n— William Shakespeare",
|
||||
"Custom Theme": "Benutzerdefiniertes Thema",
|
||||
"Theme Name": "Themenname",
|
||||
@@ -231,7 +200,7 @@
|
||||
"Background Color": "Hintergrundfarbe",
|
||||
"Preview": "Vorschau",
|
||||
"Contrast": "Kontrast",
|
||||
"Sunset": "Sonnenuntergang",
|
||||
"Sunset": "Abend",
|
||||
"Double Border": "Doppelter Rand",
|
||||
"Border Color": "Randfarbe",
|
||||
"Border Frame": "Rahmenrand",
|
||||
@@ -266,13 +235,13 @@
|
||||
"Clear Search": "Suche löschen",
|
||||
"Header & Footer": "Kopf- & Fußzeile",
|
||||
"Apply also in Scrolled Mode": "Auch im Scroll-Modus anwenden",
|
||||
"A new version of Readest is Available!": "Eine neue Version von Readest ist verfügbar!",
|
||||
"A new version of Readest is available!": "Eine neue Version von Readest ist verfügbar!",
|
||||
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} ist verfügbar (installierte Version {{currentVersion}}).",
|
||||
"Download and install now?": "Jetzt herunterladen und installieren?",
|
||||
"Downloading {{downloaded}} of {{contentLength}}": "Herunterladen {{downloaded}} von {{contentLength}}",
|
||||
"Download finished": "Download abgeschlossen",
|
||||
"DOWNLOAD & INSTALL": "HERUNTERLADEN & INSTALLIEREN",
|
||||
"Changelog:": "Änderungsprotokoll:",
|
||||
"Changelog": "Änderungsprotokoll",
|
||||
"Software Update": "Software-Update",
|
||||
"Title": "Titel",
|
||||
"Date Read": "Datum gelesen",
|
||||
@@ -281,10 +250,9 @@
|
||||
"Ascending": "Aufsteigend",
|
||||
"Descending": "Absteigend",
|
||||
"Sort by...": "Sortieren nach...",
|
||||
"Added:": "Hinzugefügt:",
|
||||
"Format:": "Format:",
|
||||
"Added": "Hinzugefügt",
|
||||
"GuanKiapTsingKhai-T": "GuanKiapTsingKhai-T",
|
||||
"Description:": "Beschreibung:",
|
||||
"Description": "Beschreibung",
|
||||
"No description available": "Keine Beschreibung verfügbar",
|
||||
"List": "Liste",
|
||||
"Grid": "Raster",
|
||||
@@ -301,7 +269,7 @@
|
||||
"Checking for updates...": "Auf Updates prüfen...",
|
||||
"Error checking for updates": "Fehler beim Überprüfen auf Updates",
|
||||
"Details": "Details",
|
||||
"File Size:": "Dateigröße:",
|
||||
"File Size": "Dateigröße",
|
||||
"Auto Detect": "Automatisch erkennen",
|
||||
"Next Section": "Nächster Abschnitt",
|
||||
"Previous Section": "Vorheriger Abschnitt",
|
||||
@@ -320,10 +288,10 @@
|
||||
"Invert Image In Dark Mode": "Invertiere Bild im Dunkeln",
|
||||
"Help improve Readest": "Readest verbessern helfen",
|
||||
"Sharing anonymized statistics": "Anonymisierte Statistiken teilen",
|
||||
"Interface Language": "Benutzeroberflächensprache",
|
||||
"Interface Language": "UI-Sprache",
|
||||
"Translation": "Übersetzung",
|
||||
"Enable Translation": "Übersetzung aktivieren",
|
||||
"Translation Service": "Übersetzungsdienst",
|
||||
"Translation Service": "Übersetzer",
|
||||
"Translate To": "Übersetzen nach",
|
||||
"Disable Translation": "Übersetzung deaktivieren",
|
||||
"Scroll": "Scrollen",
|
||||
@@ -342,14 +310,12 @@
|
||||
"Synced at {{time}}": "Synchronisiert um {{time}}",
|
||||
"Never synced": "Nie synchronisiert",
|
||||
"Show Remaining Time": "Verbleibende Zeit anzeigen",
|
||||
"Show Page Number": "Seitenzahl anzeigen",
|
||||
"{{time}} min left in chapter": "{{time}} min verbleibend im Kapitel",
|
||||
"Override Book Color": "Farbe des Buches überschreiben",
|
||||
"Login Required": "Anmeldung nötig",
|
||||
"Quota Exceeded": "Limit überschritten",
|
||||
"{{percentage}}% of Daily Translation Characters Used.": "{{percentage}}% der täglichen Übersetzungszeichen verwendet.",
|
||||
"Translation Characters": "Übersetzungszeichen",
|
||||
"Daily translation quota reached. Select another translate service to proceed.": "Das tägliche Übersetzungslimit wurde erreicht. Wählen Sie einen anderen Übersetzungsdienst, um fortzufahren.",
|
||||
"{{engine}}: {{count}} voices_one": "{{engine}}: {{count}} Stimme",
|
||||
"{{engine}}: {{count}} voices_other": "{{engine}}: {{count}} Stimmen",
|
||||
"Something went wrong. Don't worry, our team has been notified and we're working on a fix.": "Etwas ist schief gelaufen. Keine Sorge, unser Team wurde benachrichtigt und arbeitet an einer Lösung.",
|
||||
@@ -359,5 +325,184 @@
|
||||
"Contact Support": "Support kontaktieren",
|
||||
"Code Highlighting": "Code-Hervorhebung",
|
||||
"Enable Highlighting": "Code-Hervorhebung aktivieren",
|
||||
"Code Language": "Programmiersprache"
|
||||
"Code Language": "Programmiersprache",
|
||||
"Top Margin (px)": "Oberer Rand (px)",
|
||||
"Bottom Margin (px)": "Unterer Rand (px)",
|
||||
"Right Margin (px)": "Rechter Rand (px)",
|
||||
"Left Margin (px)": "Linker Rand (px)",
|
||||
"Column Gap (%)": "Spaltenabstand (%)",
|
||||
"Always Show Status Bar": "Statusleiste immer anzeigen",
|
||||
"Translation Not Available": "Übersetzung nicht verfügbar",
|
||||
"Custom Content CSS": "Benutzerdefiniertes Inhalts-CSS",
|
||||
"Enter CSS for book content styling...": "CSS für die Buchinhaltsgestaltung eingeben...",
|
||||
"Custom Reader UI CSS": "Benutzerdefiniertes CSS für die Leseroberfläche",
|
||||
"Enter CSS for reader interface styling...": "CSS für die Leseroberfläche eingeben...",
|
||||
"Crop": "Zuschneiden",
|
||||
"Book Covers": "Buchcover",
|
||||
"Fit": "Anpassen",
|
||||
"Reset {{settings}}": "{{settings}} zurücksetzen",
|
||||
"Reset Settings": "Einstellungen zurücksetzen",
|
||||
"{{count}} pages left in chapter_one": "{{count}} Seite verbleibend im Kapitel",
|
||||
"{{count}} pages left in chapter_other": "{{count}} Seiten verbleibend im Kapitel",
|
||||
"Show Remaining Pages": "Verbleibende Seiten anzeigen",
|
||||
"Source Han Serif CN VF": "Source Han Serif",
|
||||
"Huiwen-mincho": "Huiwen Mincho",
|
||||
"KingHwa_OldSong": "KingHwa Song",
|
||||
"Manage Subscription": "Abo verwalten",
|
||||
"Coming Soon": "Demnächst verfügbar",
|
||||
"Upgrade to {{plan}}": "Upgrade auf {{plan}}",
|
||||
"Upgrade to Plus or Pro": "Upgrade auf Plus oder Pro",
|
||||
"Current Plan": "Aktueller Tarif",
|
||||
"Plan Limits": "Tariflimits",
|
||||
"Processing your payment...": "Zahlung wird verarbeitet...",
|
||||
"Please wait while we confirm your subscription.": "Bitte warten Sie, während wir Ihr Abo bestätigen.",
|
||||
"Payment Processing": "Zahlung wird verarbeitet",
|
||||
"Your payment is being processed. This usually takes a few moments.": "Ihre Zahlung wird verarbeitet. Dies dauert normalerweise nur wenige Augenblicke.",
|
||||
"Payment Failed": "Zahlung fehlgeschlagen",
|
||||
"We couldn't process your subscription. Please try again or contact support if the issue persists.": "Ihr Abo konnte nicht verarbeitet werden. Bitte versuchen Sie es erneut oder kontaktieren Sie den Support, falls das Problem weiterhin besteht.",
|
||||
"Back to Profile": "Zurück zum Profil",
|
||||
"Subscription Successful!": "Abo erfolgreich!",
|
||||
"Thank you for your subscription! Your payment has been processed successfully.": "Vielen Dank für Ihr Abo! Die Zahlung wurde erfolgreich verarbeitet.",
|
||||
"Email:": "E-Mail:",
|
||||
"Plan:": "Tarif:",
|
||||
"Amount:": "Betrag:",
|
||||
"Subscription ID:": "Abo-ID:",
|
||||
"Go to Library": "Zur Bibliothek",
|
||||
"Need help? Contact our support team at support@readest.com": "Brauchen Sie Hilfe? Kontaktieren Sie unser Support-Team unter support@readest.com",
|
||||
"Free Plan": "Gratis-Tarif",
|
||||
"month": "Monat",
|
||||
"AI Translations (per day)": "KI-Übersetzungen (pro Tag)",
|
||||
"Plus Plan": "Plus-Tarif",
|
||||
"Includes All Free Plan Benefits": "Enthält alle Vorteile des Gratis-Tarifs",
|
||||
"Pro Plan": "Pro-Tarif",
|
||||
"More AI Translations": "Mehr KI-Übersetzungen",
|
||||
"Complete Your Subscription": "Abschließen Sie Ihr Abonnement",
|
||||
"{{percentage}}% of Cloud Sync Space Used.": "{{percentage}}% des Cloud-Synchronisierungsspeichers verwendet.",
|
||||
"Cloud Sync Storage": "Cloud-Speicher",
|
||||
"Parallel Read": "Paralleles Lesen",
|
||||
"Disable": "Deaktivieren",
|
||||
"Enable": "Aktivieren",
|
||||
"Upgrade to Readest Premium": "Upgrade auf Readest Premium",
|
||||
"Show Source Text": "Quelltext anzeigen",
|
||||
"Cross-Platform Sync": "Plattformübergreifende Synchronisierung",
|
||||
"Seamlessly sync your library, progress, highlights, and notes across all your devices—never lose your place again.": "Synchronisiere Bibliothek, Fortschritt und Notizen auf allen Geräten.",
|
||||
"Customizable Reading": "Individuelles Lesen",
|
||||
"AI Read Aloud": "KI-Vorlesen",
|
||||
"Enjoy hands-free reading with natural-sounding AI voices that bring your books to life.": "Höre Bücher mit natürlichen KI-Stimmen.",
|
||||
"AI Translations": "KI-Übersetzungen",
|
||||
"Connect with fellow readers and get help fast in our friendly community channels.": "Tausche dich mit anderen aus und erhalte Hilfe.",
|
||||
"Unlimited AI Read Aloud Hours": "Unbegrenzte Vorlesezeit",
|
||||
"Listen without limits—convert as much text as you like into immersive audio.": "Beliebig viel Text in Audio umwandeln.",
|
||||
"Unlock enhanced translation capabilities with more daily usage and advanced options.": "Erweitere Übersetzungen und tägliches Kontingent.",
|
||||
"DeepL Pro Access": "DeepL Pro-Zugang",
|
||||
"Enjoy faster responses and dedicated assistance whenever you need help.": "Erhalte schnellen, persönlichen Support.",
|
||||
"Daily translation quota reached. Upgrade your plan to continue using AI translations.": "Tageslimit erreicht. Upgrade nötig, um weiter zu übersetzen.",
|
||||
"Personalize every detail with adjustable fonts, layouts, themes, and advanced display settings for the perfect reading experience.": "Personalisieren Sie jedes Detail mit anpassbaren Schriften, Layouts, Themes und erweiterten Anzeigeeinstellungen für das perfekte Leseerlebnis.",
|
||||
"Translate any text instantly with the power of Google, Azure, or DeepL—understand content in any language.": "Übersetzen Sie jeden Text sofort mit der Kraft von Google, Azure oder DeepL—verstehen Sie Inhalte in jeder Sprache.",
|
||||
"Includes All Plus Plan Benefits": "Enthält alle Plus-Plan-Vorteile",
|
||||
"Early Feature Access": "Früher Zugang zu Features",
|
||||
"Be the first to explore new features, updates, and innovations before anyone else.": "Entdecken Sie neue Features, Updates und Innovationen vor allen anderen.",
|
||||
"Advanced AI Tools": "Erweiterte KI-Tools",
|
||||
"Harness powerful AI tools for smarter reading, translation, and content discovery.": "Nutzen Sie leistungsstarke KI-Tools für intelligenteres Lesen, Übersetzen und Content-Discovery.",
|
||||
"Translate up to 100,000 characters daily with the most accurate translation engine available.": "Übersetzen Sie täglich bis zu 100.000 Zeichen mit der genauesten verfügbaren Übersetzungsmaschine.",
|
||||
"Translate up to 500,000 characters daily with the most accurate translation engine available.": "Übersetzen Sie täglich bis zu 500.000 Zeichen mit der genauesten verfügbaren Übersetzungsmaschine.",
|
||||
"Securely store and access your entire reading collection with up to 5 GB of cloud storage.": "Speichern und greifen Sie sicher auf Ihre gesamte Lesekollektion mit bis zu 5 GB Cloud-Speicher zu.",
|
||||
"Securely store and access your entire reading collection with up to 20 GB of cloud storage.": "Speichern und greifen Sie sicher auf Ihre gesamte Lesekollektion mit bis zu 20 GB Cloud-Speicher zu.",
|
||||
"Deleted cloud backup of the book: {{title}}": "Gelöschte Cloud-Sicherung des Buches: {{title}}",
|
||||
"Are you sure to delete the cloud backup of the selected book?": "Sind Sie sicher, dass Sie die Cloud-Sicherung des ausgewählten Buches löschen möchten?",
|
||||
"What's New in Readest": "Was gibt es Neues in Readest",
|
||||
"Enter book title": "Buchtitel eingeben",
|
||||
"Subtitle": "Untertitel",
|
||||
"Enter book subtitle": "Buchuntertitel eingeben",
|
||||
"Enter author name": "Autorennamen eingeben",
|
||||
"Series": "Serie",
|
||||
"Enter series name": "Seriennamen eingeben",
|
||||
"Series Index": "Seriennummer",
|
||||
"Enter series index": "Seriennummer eingeben",
|
||||
"Total in Series": "Gesamt in Serie",
|
||||
"Enter total books in series": "Gesamtanzahl der Bücher in Serie eingeben",
|
||||
"Enter publisher": "Verlag eingeben",
|
||||
"Publication Date": "Veröffentlichungsdatum",
|
||||
"Identifier": "Identifikator",
|
||||
"Enter book description": "Buchbeschreibung eingeben",
|
||||
"Change cover image": "Titelbild ändern",
|
||||
"Replace": "Ersetzen",
|
||||
"Unlock cover": "Titelbild entsperren",
|
||||
"Lock cover": "Titelbild sperren",
|
||||
"Auto-Retrieve Metadata": "Metadaten automatisch abrufen",
|
||||
"Auto-Retrieve": "Automatisch abrufen",
|
||||
"Unlock all fields": "Alle Felder entsperren",
|
||||
"Unlock All": "Alle entsperren",
|
||||
"Lock all fields": "Alle Felder sperren",
|
||||
"Lock All": "Alle sperren",
|
||||
"Reset": "Zurücksetzen",
|
||||
"Edit Metadata": "Metadaten bearbeiten",
|
||||
"Locked": "Gesperrt",
|
||||
"Select Metadata Source": "Metadatenquelle auswählen",
|
||||
"Keep manual input": "Manuelle Eingabe beibehalten",
|
||||
"Google Books": "Google Books",
|
||||
"Open Library": "Open Library",
|
||||
"Fiction, Science, History": "Belletristik, Wissenschaft, Geschichte",
|
||||
"Select Cover Image": "Coverbild auswählen",
|
||||
"Open Book in New Window": "Buch in neuem Fenster öffnen",
|
||||
"Voices for {{lang}}": "Stimmen für {{lang}}",
|
||||
"Yandex Translate": "Yandex Übersetzer",
|
||||
"YYYY or YYYY-MM-DD": "YYYY oder YYYY-MM-DD",
|
||||
"Restore Purchase": "Kauf wiederherstellen",
|
||||
"No purchases found to restore.": "Keine Käufe zum Wiederherstellen gefunden.",
|
||||
"Failed to restore purchases.": "Wiederherstellung der Käufe fehlgeschlagen.",
|
||||
"Failed to manage subscription.": "Verwaltung des Abonnements fehlgeschlagen.",
|
||||
"Failed to load subscription plans.": "Laden der Abonnementpläne fehlgeschlagen.",
|
||||
"year": "Jahr",
|
||||
"Failed to create checkout session": "Fehler beim Erstellen der Checkout-Sitzung",
|
||||
"Storage": "Speicher",
|
||||
"Terms of Service": "Allgemeine Geschäftsbedingungen",
|
||||
"Privacy Policy": "Datenschutzbestimmungen",
|
||||
"Disable Double Click": "Doppelklick deaktivieren",
|
||||
"TTS not supported for this document": "TTS wird für dieses Dokument nicht unterstützt",
|
||||
"Reset Password": "Passwort zurücksetzen",
|
||||
"Show Reading Progress": "Lesefortschritt anzeigen",
|
||||
"Reading Progress Style": "Lesefortschritt-Stil",
|
||||
"Page Number": "Seitenzahl",
|
||||
"Percentage": "Prozentsatz",
|
||||
"Deleted local copy of the book: {{title}}": "Gelöschte lokale Kopie des Buches: {{title}}",
|
||||
"Failed to delete cloud backup of the book: {{title}}": "Fehler beim Löschen der Cloud-Sicherung des Buches: {{title}}",
|
||||
"Failed to delete local copy of the book: {{title}}": "Fehler beim Löschen der lokalen Kopie des Buches: {{title}}",
|
||||
"Are you sure to delete the local copy of the selected book?": "Sind Sie sicher, dass Sie die lokale Kopie des ausgewählten Buches löschen möchten?",
|
||||
"Remove from Cloud & Device": "Aus Cloud & Gerät entfernen",
|
||||
"Remove from Cloud Only": "Nur aus der Cloud entfernen",
|
||||
"Remove from Device Only": "Nur vom Gerät entfernen",
|
||||
"Disconnected": "Getrennt",
|
||||
"KOReader Sync Settings": "KOReader Sync-Einstellungen",
|
||||
"Sync Strategy": "Sync-Strategie",
|
||||
"Ask on conflict": "Bei Konflikten fragen",
|
||||
"Always use latest": "Immer die neueste Version verwenden",
|
||||
"Send changes only": "Nur Änderungen senden",
|
||||
"Receive changes only": "Nur Änderungen empfangen",
|
||||
"Disabled": "Deaktiviert",
|
||||
"Checksum Method": "Prüfziffernverfahren",
|
||||
"File Content (recommended)": "Dateiinhalte (empfohlen)",
|
||||
"File Name": "Dateiname",
|
||||
"Device Name": "Gerätename",
|
||||
"Sync Tolerance": "Sync-Toleranz",
|
||||
"Connect to your KOReader Sync server.": "Mit Ihrem KOReader Sync-Server verbinden.",
|
||||
"Server URL": "Server-URL",
|
||||
"Username": "Benutzername",
|
||||
"Your Username": "Ihr Benutzername",
|
||||
"Password": "Passwort",
|
||||
"Connect": "Verbinden",
|
||||
"KOReader Sync": "KOReader Sync",
|
||||
"Sync Conflict": "Sync-Konflikt",
|
||||
"Sync reading progress from \"{{deviceName}}\"?": "Lesefortschritt von \"{{deviceName}}\" synchronisieren?",
|
||||
"another device": "ein anderes Gerät",
|
||||
"Local Progress": "Lokaler Fortschritt",
|
||||
"Remote Progress": "Entfernter Fortschritt",
|
||||
"Page {{page}} of {{total}} ({{percentage}}%)": "Seite {{page}} von {{total}} ({{percentage}}%)",
|
||||
"Current position": "Aktuelle Position",
|
||||
"Approximately page {{page}} of {{total}} ({{percentage}}%)": "Ungefähr Seite {{page}} von {{total}} ({{percentage}}%)",
|
||||
"Approximately {{percentage}}%": "Ungefähr {{percentage}}%",
|
||||
"Failed to connect": "Verbindung fehlgeschlagen",
|
||||
"Sync Server Connected": "Sync-Server verbunden",
|
||||
"Precision: {{precision}} digits after the decimal": "Präzision: {{precision}} Nachkommastellen",
|
||||
"Sync as {{userDisplayName}}": "Sync als {{userDisplayName}}"
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
"Confirm Deletion": "Επιβεβαίωση διαγραφής",
|
||||
"Copied to notebook": "Αντιγράφηκε στο σημειωματάριο",
|
||||
"Copy": "Αντιγραφή",
|
||||
"Custom CSS": "Προσαρμοσμένο CSS",
|
||||
"Dark Mode": "Σκοτεινή λειτουργία",
|
||||
"Default": "Προεπιλογή",
|
||||
"Default Font": "Προεπιλεγμένη γραμματοσειρά",
|
||||
@@ -28,7 +27,6 @@
|
||||
"Dictionary": "Λεξικό",
|
||||
"Download Readest": "Λήψη Readest",
|
||||
"Edit": "Επεξεργασία",
|
||||
"Enter your custom CSS here...": "Εισάγετε το προσαρμοσμένο CSS εδώ...",
|
||||
"Excerpts": "Αποσπάσματα",
|
||||
"Failed to import book(s): {{filenames}}": "Αποτυχία εισαγωγής βιβλίου(ων): {{filenames}}",
|
||||
"Fast": "Γρήγορα",
|
||||
@@ -48,7 +46,6 @@
|
||||
"Horizontal Direction": "Οριζόντια κατεύθυνση",
|
||||
"Hyphenation": "Συλλαβισμός",
|
||||
"Import Books": "Εισαγωγή βιβλίων",
|
||||
"Language:": "Γλώσσα:",
|
||||
"Layout": "Διάταξη",
|
||||
"Light Mode": "Φωτεινή λειτουργία",
|
||||
"Loading...": "Φόρτωση...",
|
||||
@@ -70,8 +67,8 @@
|
||||
"Paging Animation": "Κινούμενα σχέδια σελιδοποίησης",
|
||||
"Paragraph": "Παράγραφος",
|
||||
"Parallel Read": "Παράλληλη ανάγνωση",
|
||||
"Published:": "Δημοσιεύτηκε:",
|
||||
"Publisher:": "Εκδότης:",
|
||||
"Published": "Δημοσιεύτηκε",
|
||||
"Publisher": "Εκδότης",
|
||||
"Reading Progress Synced": "Ο συγχρονισμός προόδου ανάγνωσης ολοκληρώθηκε",
|
||||
"Reload Page": "Επαναφόρτωση σελίδας",
|
||||
"Reveal in File Explorer": "Εμφάνιση στον εξερευνητή αρχείων",
|
||||
@@ -96,16 +93,15 @@
|
||||
"Slow": "Αργά",
|
||||
"Solarized": "Solarized",
|
||||
"Speak": "Ομιλία",
|
||||
"Subjects:": "Θέματα:",
|
||||
"Subjects": "Θέματα",
|
||||
"System Fonts": "Γραμματοσειρές συστήματος",
|
||||
"TTS not supported in this device": "Η μετατροπή κειμένου σε ομιλία δεν υποστηρίζεται σε αυτή τη συσκευή",
|
||||
"Theme Color": "Χρώμα θέματος",
|
||||
"Theme Mode": "Λειτουργία θέματος",
|
||||
"Translate": "Μετάφραση",
|
||||
"Translated Text": "Μεταφρασμένο κείμενο",
|
||||
"Unknown": "Άγνωστο",
|
||||
"Untitled": "Χωρίς τίτλο",
|
||||
"Updated:": "Ενημερώθηκε:",
|
||||
"Updated": "Ενημερώθηκε",
|
||||
"Version {{version}}": "Έκδοση {{version}}",
|
||||
"Vertical Direction": "Κάθετη κατεύθυνση",
|
||||
"Welcome to your library. You can import your books here and read them anytime.": "Καλώς ήρθατε στη βιβλιοθήκη σας. Μπορείτε να εισάγετε τα βιβλία σας εδώ και να τα διαβάσετε οποιαδήποτε στιγμή.",
|
||||
@@ -114,8 +110,6 @@
|
||||
"Your Library": "Η βιβλιοθήκη σας",
|
||||
"TTS not supported for PDF": "Η μετατροπή κειμένου σε ομιλία δεν υποστηρίζεται για αρχεία PDF",
|
||||
"Override Book Font": "Παράκαμψη γραμματοσειράς βιβλίου",
|
||||
"Vertical Margins (px)": "Κάθετα περιθώρια (px)",
|
||||
"Horizontal Margins (%)": "Οριζόντια περιθώρια (%)",
|
||||
"Apply to All Books": "Εφαρμογή σε όλα τα βιβλία",
|
||||
"Apply to This Book": "Εφαρμογή σε αυτό το βιβλίο",
|
||||
"Unable to fetch the translation. Try again later.": "Αδυναμία λήψης της μετάφρασης. Δοκιμάστε ξανά αργότερα.",
|
||||
@@ -130,8 +124,6 @@
|
||||
"Failed to download book: {{title}}": "Αποτυχία λήψης βιβλίου: {{title}}",
|
||||
"Upload Book": "Ανέβασμα βιβλίου",
|
||||
"Auto Upload Books to Cloud": "Αυτόματη αποθήκευση στο cloud",
|
||||
"{{percentage}}% of Cloud Storage Used.": "{{percentage}}% της αποθήκευσης στο cloud χρησιμοποιήθηκε.",
|
||||
"Storage": "Αποθήκευση",
|
||||
"Book deleted: {{title}}": "Το βιβλίο με τίτλο {{title}} διαγράφηκε",
|
||||
"Failed to delete book: {{title}}": "Αποτυχία διαγραφής βιβλίου: {{title}}",
|
||||
"Check Updates on Start": "Αυτόματος έλεγχος ενημερώσεων",
|
||||
@@ -163,7 +155,7 @@
|
||||
"Create a Password": "Δημιουργία κωδικού πρόσβασης",
|
||||
"Sign up": "Εγγραφή",
|
||||
"Signing up...": "Εγγραφή...",
|
||||
"Don’t have an account? Sign up": "Δεν έχετε λογαριασμό; Εγγραφείτε",
|
||||
"Don't have an account? Sign up": "Δεν έχετε λογαριασμό; Εγγραφείτε",
|
||||
"Check your email for the confirmation link": "Ελέγξτε το email σας για τον σύνδεσμο επιβεβαίωσης",
|
||||
"Signing in ...": "Σύνδεση ...",
|
||||
"Send a magic link email": "Αποστολή email με μαγικό σύνδεσμο",
|
||||
@@ -187,35 +179,13 @@
|
||||
"Sign in with GitHub": "Σύνδεση με GitHub",
|
||||
"Account": "Λογαριασμός",
|
||||
"Failed to delete user. Please try again later.": "Αποτυχία διαγραφής χρήστη. Παρακαλώ δοκιμάστε ξανά αργότερα.",
|
||||
"Unlimited Offline/Online Reading": "Απεριόριστη ανάγνωση εκτός/εντός σύνδεσης",
|
||||
"Unlimited Cloud Sync Devices": "Απεριόριστες συσκευές συγχρονισμού Cloud",
|
||||
"Essential Text-to-Speech Voices": "Βασικές φωνές μετατροπής κειμένου σε ομιλία",
|
||||
"DeepL Free Access": "Δωρεάν πρόσβαση DeepL",
|
||||
"Community Support": "Υποστήριξη κοινότητας",
|
||||
"500 MB Cloud Sync Space": "500 MB χώρος συγχρονισμού Cloud",
|
||||
"Includes All Free Tier Benefits": "Περιλαμβάνει όλα τα οφέλη της δωρεάν βαθμίδας",
|
||||
"AI Summaries": "Περιλήψεις AI",
|
||||
"AI Translations": "Μεταφράσεις AI",
|
||||
"Priority Support": "Υποστήριξη προτεραιότητας",
|
||||
"DeepL Pro Access": "Πρόσβαση DeepL Pro",
|
||||
"Expanded Text-to-Speech Voices": "Διευρυμένες φωνές μετατροπής κειμένου σε ομιλία",
|
||||
"2000 MB Cloud Sync Space": "2000 MB χώρος συγχρονισμού Cloud",
|
||||
"Includes All Plus Tier Benefits": "Περιλαμβάνει όλα τα οφέλη της βαθμίδας Plus",
|
||||
"Unlimited AI Summaries": "Απεριόριστες περιλήψεις AI",
|
||||
"Unlimited AI Translations": "Απεριόριστες μεταφράσεις AI",
|
||||
"Unlimited DeepL Pro Access": "Απεριόριστη πρόσβαση DeepL Pro",
|
||||
"Advanced AI Tools": "Προηγμένα εργαλεία AI",
|
||||
"Early Feature Access": "Πρώιμη πρόσβαση σε λειτουργίες",
|
||||
"10 GB Cloud Sync Space": "10 GB χώρος συγχρονισμού Cloud",
|
||||
"Loading profile...": "Φόρτωση προφίλ...",
|
||||
"Features": "Χαρακτηριστικά",
|
||||
"Delete Account": "Διαγραφή λογαριασμού",
|
||||
"Delete Your Account?": "Διαγραφή του λογαριασμού σας;",
|
||||
"This action cannot be undone. All your data in the cloud will be permanently deleted.": "Αυτή η ενέργεια δεν μπορεί να αναιρεθεί. Όλα τα δεδομένα σας στο cloud θα διαγραφούν μόνιμα.",
|
||||
"Delete Permanently": "Μόνιμη διαγραφή",
|
||||
"Free Tier": "Δωρεάν βαθμίδα",
|
||||
"Plus Tier": "Βαθμίδα Plus",
|
||||
"Pro Tier": "Βαθμίδα Pro",
|
||||
"RTL Direction": "Κατεύθυνση RTL",
|
||||
"Maximum Column Height": "Μέγιστο ύψος στήλης",
|
||||
"Maximum Column Width": "Μέγιστο πλάτος στήλης",
|
||||
@@ -266,13 +236,13 @@
|
||||
"Clear Search": "Καθαρισμός αναζήτησης",
|
||||
"Header & Footer": "Κεφαλίδα & Υποσέλιδο",
|
||||
"Apply also in Scrolled Mode": "Εφαρμογή και σε λειτουργία κύλισης",
|
||||
"A new version of Readest is Available!": "Μια νέα έκδοση του Readest είναι διαθέσιμη!",
|
||||
"A new version of Readest is available!": "Μια νέα έκδοση του Readest είναι διαθέσιμη!",
|
||||
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Η έκδοση Readest {{newVersion}} είναι διαθέσιμη (έκδοση που είναι εγκατεστημένη {{currentVersion}}).",
|
||||
"Download and install now?": "Θέλετε να την κατεβάσετε και να την εγκαταστήσετε τώρα;",
|
||||
"Downloading {{downloaded}} of {{contentLength}}": "Κατεβάζω {{downloaded}} από {{contentLength}}",
|
||||
"Download finished": "Η λήψη ολοκληρώθηκε",
|
||||
"DOWNLOAD & INSTALL": "ΛΗΨΗ & ΕΓΚΑΤΑΣΤΑΣΗ",
|
||||
"Changelog:": "Αλλαγές:",
|
||||
"Changelog": "Αλλαγές",
|
||||
"Software Update": "Ενημέρωση λογισμικού",
|
||||
"Title": "Τίτλος",
|
||||
"Date Read": "Ημερομηνία ανάγνωσης",
|
||||
@@ -281,10 +251,9 @@
|
||||
"Ascending": "Αύξουσα",
|
||||
"Descending": "Φθίνουσα",
|
||||
"Sort by...": "Ταξινόμηση κατά...",
|
||||
"Added:": "Προστέθηκε:",
|
||||
"Format:": "Μορφή:",
|
||||
"Added": "Προστέθηκε",
|
||||
"GuanKiapTsingKhai-T": "GuanKiapTsingKhai-T",
|
||||
"Description:": "Περιγραφή:",
|
||||
"Description": "Περιγραφή",
|
||||
"No description available": "Δεν είναι διαθέσιμη καμία περιγραφή",
|
||||
"List": "Λίστα",
|
||||
"Grid": "Πλέγμα",
|
||||
@@ -301,7 +270,7 @@
|
||||
"Checking for updates...": "Έλεγχος για ενημερώσεις...",
|
||||
"Error checking for updates": "Σφάλμα κατά τον έλεγχο για ενημερώσεις",
|
||||
"Details": "Πληρ.",
|
||||
"File Size:": "Μέγεθος αρχείου:",
|
||||
"File Size": "Μέγεθος αρχείου",
|
||||
"Auto Detect": "Αυτόματη ανίχνευση",
|
||||
"Next Section": "Επόμενη ενότητα",
|
||||
"Previous Section": "Προηγούμενη ενότητα",
|
||||
@@ -342,14 +311,12 @@
|
||||
"Synced at {{time}}": "Συγχρονίστηκε στις {{time}}",
|
||||
"Never synced": "Ποτέ δεν συγχρονίστηκε",
|
||||
"Show Remaining Time": "Εμφάνιση υπολειπόμενου χρόνου",
|
||||
"Show Page Number": "Εμφάνιση αριθμού σελίδας",
|
||||
"{{time}} min left in chapter": "{{time}} λεπτά απομένουν στο κεφάλαιο",
|
||||
"Override Book Color": "Παράκαμψη χρώματος βιβλίου",
|
||||
"Login Required": "Απαιτείται σύνδεση",
|
||||
"Quota Exceeded": "Υπέρβαση ορίου",
|
||||
"{{percentage}}% of Daily Translation Characters Used.": "{{percentage}}% των ημερήσιων χαρακτήρων μετάφρασης χρησιμοποιήθηκαν.",
|
||||
"Translation Characters": "Χαρακτήρες μετάφρασης",
|
||||
"Daily translation quota reached. Select another translate service to proceed.": "Έχετε φτάσει το ημερήσιο όριο χαρακτήρων μετάφρασης. Επιλέξτε άλλη υπηρεσία μετάφρασης για να συνεχίσετε.",
|
||||
"{{engine}}: {{count}} voices_one": "{{engine}}: {{count}} φωνή",
|
||||
"{{engine}}: {{count}} voices_other": "{{engine}}: {{count}} φωνές",
|
||||
"Something went wrong. Don't worry, our team has been notified and we're working on a fix.": "Κάτι πήγε στραβά. Μην ανησυχείτε, η ομάδα μας έχει ενημερωθεί και εργαζόμαστε για μια λύση.",
|
||||
@@ -359,5 +326,183 @@
|
||||
"Contact Support": "Επικοινωνήστε με την υποστήριξη",
|
||||
"Code Highlighting": "Επισήμανση κώδικα",
|
||||
"Enable Highlighting": "Ενεργοποίηση επισήμανσης",
|
||||
"Code Language": "Γλώσσα κώδικα"
|
||||
"Code Language": "Γλώσσα κώδικα",
|
||||
"Top Margin (px)": "Πάνω περιθώριο (px)",
|
||||
"Bottom Margin (px)": "Κάτω περιθώριο (px)",
|
||||
"Right Margin (px)": "Δεξί περιθώριο (px)",
|
||||
"Left Margin (px)": "Αριστερό περιθώριο (px)",
|
||||
"Column Gap (%)": "Απόσταση στηλών (%)",
|
||||
"Always Show Status Bar": "Πάντα εμφάνιση γραμμής κατάστασης",
|
||||
"Translation Not Available": "Η μετάφραση δεν είναι διαθέσιμη",
|
||||
"Custom Content CSS": "Προσαρμοσμένο CSS περιεχομένου",
|
||||
"Enter CSS for book content styling...": "Εισαγάγετε CSS για στυλ περιεχομένου βιβλίου...",
|
||||
"Custom Reader UI CSS": "Προσαρμοσμένο CSS διεπαφής αναγνώστη",
|
||||
"Enter CSS for reader interface styling...": "Εισαγάγετε CSS για στυλ διεπαφής αναγνώστη...",
|
||||
"Crop": "Περικοπή",
|
||||
"Book Covers": "Εξώφυλλα βιβλίων",
|
||||
"Fit": "Προσαρμογή",
|
||||
"Reset {{settings}}": "Επαναφορά {{settings}}",
|
||||
"Reset Settings": "Επαναφορά ρυθμίσεων",
|
||||
"{{count}} pages left in chapter_one": "Μένει {{count}} σελίδα",
|
||||
"{{count}} pages left in chapter_other": "Μένουν {{count}} σελίδες",
|
||||
"Show Remaining Pages": "Εμφάνιση υπολοίπων",
|
||||
"Source Han Serif CN VF": "Source Han Serif",
|
||||
"Huiwen-mincho": "Huiwen Mincho",
|
||||
"KingHwa_OldSong": "KingHwa Song",
|
||||
"Manage Subscription": "Διαχείριση συνδρομής",
|
||||
"Coming Soon": "Έρχεται σύντομα",
|
||||
"Upgrade to {{plan}}": "Αναβάθμιση σε {{plan}}",
|
||||
"Upgrade to Plus or Pro": "Αναβάθμιση σε Plus ή Pro",
|
||||
"Current Plan": "Τρέχον πρόγραμμα",
|
||||
"Plan Limits": "Όρια προγράμματος",
|
||||
"Processing your payment...": "Επεξεργασία πληρωμής...",
|
||||
"Please wait while we confirm your subscription.": "Παρακαλώ περιμένετε όσο επιβεβαιώνουμε τη συνδρομή σας.",
|
||||
"Payment Processing": "Επεξεργασία πληρωμής",
|
||||
"Your payment is being processed. This usually takes a few moments.": "Η πληρωμή σας επεξεργάζεται. Συνήθως διαρκεί λίγες στιγμές.",
|
||||
"Payment Failed": "Η πληρωμή απέτυχε",
|
||||
"We couldn't process your subscription. Please try again or contact support if the issue persists.": "Δεν ήταν δυνατή η επεξεργασία της συνδρομής σας. Παρακαλώ προσπαθήστε ξανά ή επικοινωνήστε με την υποστήριξη αν το πρόβλημα συνεχιστεί.",
|
||||
"Back to Profile": "Επιστροφή στο προφίλ",
|
||||
"Subscription Successful!": "Η συνδρομή ολοκληρώθηκε!",
|
||||
"Thank you for your subscription! Your payment has been processed successfully.": "Σας ευχαριστούμε για τη συνδρομή σας! Η πληρωμή ολοκληρώθηκε με επιτυχία.",
|
||||
"Email:": "Email:",
|
||||
"Plan:": "Πρόγραμμα:",
|
||||
"Amount:": "Ποσό:",
|
||||
"Subscription ID:": "ID συνδρομής:",
|
||||
"Go to Library": "Μετάβαση στη βιβλιοθήκη",
|
||||
"Need help? Contact our support team at support@readest.com": "Χρειάζεστε βοήθεια; Επικοινωνήστε με την υποστήριξη στο support@readest.com",
|
||||
"Free Plan": "Δωρεάν πρόγραμμα",
|
||||
"month": "μήνας",
|
||||
"AI Translations (per day)": "Μεταφράσεις AI (ανά ημέρα)",
|
||||
"Plus Plan": "Πρόγραμμα Plus",
|
||||
"Includes All Free Plan Benefits": "Περιλαμβάνει όλα τα οφέλη του δωρεάν προγράμματος",
|
||||
"Pro Plan": "Πρόγραμμα Pro",
|
||||
"More AI Translations": "Περισσότερες μεταφράσεις AI",
|
||||
"Complete Your Subscription": "Ολοκληρώστε τη συνδρομή σας",
|
||||
"{{percentage}}% of Cloud Sync Space Used.": "{{percentage}}% του χώρου συγχρονισμού στο cloud χρησιμοποιήθηκε.",
|
||||
"Cloud Sync Storage": "Χώρος συγχρονισμού cloud",
|
||||
"Disable": "Απενεργοποίηση",
|
||||
"Enable": "Ενεργοποίηση",
|
||||
"Upgrade to Readest Premium": "Αναβάθμιση στο Readest Premium",
|
||||
"Show Source Text": "Εμφάνιση αρχικού κειμένου",
|
||||
"Cross-Platform Sync": "Συγχρονισμός σε όλες τις συσκευές",
|
||||
"Seamlessly sync your library, progress, highlights, and notes across all your devices—never lose your place again.": "Συγχρονίστε βιβλία, πρόοδο και σημειώσεις σε όλες τις συσκευές.",
|
||||
"Customizable Reading": "Προσαρμόσιμη ανάγνωση",
|
||||
"Personalize every detail with adjustable fonts, layouts, themes, and advanced display settings for the perfect reading experience.": "Ρυθμίστε γραμματοσειρές, διάταξη και θέματα όπως θέλετε.",
|
||||
"AI Read Aloud": "Ανάγνωση με AI",
|
||||
"Enjoy hands-free reading with natural-sounding AI voices that bring your books to life.": "Ακούστε τα βιβλία σας με φυσικές φωνές AI.",
|
||||
"AI Translations": "Μεταφράσεις με AI",
|
||||
"Translate any text instantly with the power of Google, Azure, or DeepL—understand content in any language.": "Μεταφράστε άμεσα με Google, Azure ή DeepL.",
|
||||
"Connect with fellow readers and get help fast in our friendly community channels.": "Συμμετέχετε στην κοινότητα για άμεση υποστήριξη.",
|
||||
"Unlimited AI Read Aloud Hours": "Απεριόριστη ανάγνωση με AI",
|
||||
"Listen without limits—convert as much text as you like into immersive audio.": "Απεριόριστη μετατροπή κειμένου σε ήχο.",
|
||||
"Unlock enhanced translation capabilities with more daily usage and advanced options.": "Περισσότερες μεταφράσεις και δυνατότητες.",
|
||||
"DeepL Pro Access": "Πρόσβαση στο DeepL Pro",
|
||||
"Enjoy faster responses and dedicated assistance whenever you need help.": "Γρήγορη και άμεση υποστήριξη.",
|
||||
"Daily translation quota reached. Upgrade your plan to continue using AI translations.": "Έχετε φτάσει το ημερήσιο όριο. Κάντε αναβάθμιση για περισσότερες μεταφράσεις.",
|
||||
"Includes All Plus Plan Benefits": "Περιλαμβάνει Όλα τα Οφέλη του Plus Πλάνου",
|
||||
"Early Feature Access": "Πρόωρη Πρόσβαση σε Χαρακτηριστικά",
|
||||
"Be the first to explore new features, updates, and innovations before anyone else.": "Εξερευνήστε πρώτοι νέα χαρακτηριστικά, ενημερώσεις και καινοτομίες.",
|
||||
"Advanced AI Tools": "Προηγμένα Εργαλεία AI",
|
||||
"Harness powerful AI tools for smarter reading, translation, and content discovery.": "Αξιοποιήστε ισχυρά εργαλεία AI για εξυπνότερη ανάγνωση, μετάφραση και ανακάλυψη περιεχομένου.",
|
||||
"Translate up to 100,000 characters daily with the most accurate translation engine available.": "Μεταφράστε έως 100.000 χαρακτήρες ημερησίως με τη πιο ακριβή μηχανή μετάφρασης.",
|
||||
"Translate up to 500,000 characters daily with the most accurate translation engine available.": "Μεταφράστε έως 500.000 χαρακτήρες ημερησίως με τη πιο ακριβή μηχανή μετάφρασης.",
|
||||
"Securely store and access your entire reading collection with up to 5 GB of cloud storage.": "Αποθηκεύστε και αποκτήστε πρόσβαση στη συλλογή ανάγνωσης με έως 5 GB ασφαλή cloud αποθήκευση.",
|
||||
"Securely store and access your entire reading collection with up to 20 GB of cloud storage.": "Αποθηκεύστε και αποκτήστε πρόσβαση στη συλλογή ανάγνωσης με έως 20 GB ασφαλή cloud αποθήκευση.",
|
||||
"Deleted cloud backup of the book: {{title}}": "Διαγράφηκε το αντίγραφο ασφαλείας του βιβλίου: {{title}}",
|
||||
"Are you sure to delete the cloud backup of the selected book?": "Είστε σίγουροι ότι θέλετε να διαγράψετε το αντίγραφο ασφαλείας του επιλεγμένου βιβλίου;",
|
||||
"What's New in Readest": "Τι νέο υπάρχει στο Readest",
|
||||
"Enter book title": "Εισάγετε τίτλο βιβλίου",
|
||||
"Subtitle": "Υπότιτλος",
|
||||
"Enter book subtitle": "Εισάγετε υπότιτλο βιβλίου",
|
||||
"Enter author name": "Εισάγετε όνομα συγγραφέα",
|
||||
"Series": "Σειρά",
|
||||
"Enter series name": "Εισάγετε όνομα σειράς",
|
||||
"Series Index": "Αριθμός σειράς",
|
||||
"Enter series index": "Εισάγετε αριθμό σειράς",
|
||||
"Total in Series": "Σύνολο στη σειρά",
|
||||
"Enter total books in series": "Εισάγετε συνολικό αριθμό βιβλίων στη σειρά",
|
||||
"Enter publisher": "Εισάγετε εκδότη",
|
||||
"Publication Date": "Ημερομηνία έκδοσης",
|
||||
"Identifier": "Αναγνωριστικό",
|
||||
"Enter book description": "Εισάγετε περιγραφή βιβλίου",
|
||||
"Change cover image": "Αλλαγή εικόνας εξωφύλλου",
|
||||
"Replace": "Αντικατάσταση",
|
||||
"Unlock cover": "Ξεκλείδωμα εξωφύλλου",
|
||||
"Lock cover": "Κλείδωμα εξωφύλλου",
|
||||
"Auto-Retrieve Metadata": "Αυτόματη ανάκτηση μεταδεδομένων",
|
||||
"Auto-Retrieve": "Αυτόματη ανάκτηση",
|
||||
"Unlock all fields": "Ξεκλείδωμα όλων των πεδίων",
|
||||
"Unlock All": "Ξεκλείδωμα όλων",
|
||||
"Lock all fields": "Κλείδωμα όλων των πεδίων",
|
||||
"Lock All": "Κλείδωμα όλων",
|
||||
"Reset": "Επαναφορά",
|
||||
"Edit Metadata": "Επεξεργασία μεταδεδομένων",
|
||||
"Locked": "Κλειδωμένο",
|
||||
"Select Metadata Source": "Επιλέξτε πηγή μεταδεδομένων",
|
||||
"Keep manual input": "Διατήρηση χειροκίνητης εισαγωγής",
|
||||
"Google Books": "Google Books",
|
||||
"Open Library": "Open Library",
|
||||
"Fiction, Science, History": "Μυθοπλασία, Επιστήμη, Ιστορία",
|
||||
"Select Cover Image": "Επιλέξτε εικόνα εξωφύλλου",
|
||||
"Open Book in New Window": "Άνοιγμα βιβλίου σε νέο παράθυρο",
|
||||
"Voices for {{lang}}": "Φωνές για {{lang}}",
|
||||
"Yandex Translate": "Μετάφραση Yandex",
|
||||
"YYYY or YYYY-MM-DD": "YYYY ή YYYY-MM-DD",
|
||||
"Restore Purchase": "Επαναφορά αγοράς",
|
||||
"No purchases found to restore.": "Δεν βρέθηκαν αγορές για επαναφορά.",
|
||||
"Failed to restore purchases.": "Αποτυχία επαναφοράς αγορών.",
|
||||
"Failed to manage subscription.": "Αποτυχία διαχείρισης συνδρομής.",
|
||||
"Failed to load subscription plans.": "Αποτυχία φόρτωσης σχεδίων συνδρομής.",
|
||||
"year": "έτος",
|
||||
"Failed to create checkout session": "Αποτυχία δημιουργίας συνεδρίας πληρωμής",
|
||||
"Storage": "Αποθήκευση",
|
||||
"Terms of Service": "Όροι χρήσης",
|
||||
"Privacy Policy": "Πολιτική απορρήτου",
|
||||
"Disable Double Click": "Απενεργοποίηση διπλού κλικ",
|
||||
"TTS not supported for this document": "Η τεχνολογία TTS δεν υποστηρίζεται για αυτό το έγγραφο.",
|
||||
"Reset Password": "Επαναφορά κωδικού",
|
||||
"Show Reading Progress": "Εμφάνιση προόδου ανάγνωσης",
|
||||
"Reading Progress Style": "Στυλ προόδου ανάγνωσης",
|
||||
"Page Number": "Αριθμός σελίδας",
|
||||
"Percentage": "Ποσοστό",
|
||||
"Deleted local copy of the book: {{title}}": "Διαγράφηκε το τοπικό αντίγραφο του βιβλίου: {{title}}",
|
||||
"Failed to delete cloud backup of the book: {{title}}": "Αποτυχία διαγραφής του αντιγράφου ασφαλείας του βιβλίου: {{title}}",
|
||||
"Failed to delete local copy of the book: {{title}}": "Αποτυχία διαγραφής της τοπικής αντιγράφου του βιβλίου: {{title}}",
|
||||
"Are you sure to delete the local copy of the selected book?": "Είστε σίγουροι ότι θέλετε να διαγράψετε την τοπική αντιγραφή του επιλεγμένου βιβλίου;",
|
||||
"Remove from Cloud & Device": "Αφαίρεση από το Cloud & Device",
|
||||
"Remove from Cloud Only": "Αφαίρεση μόνο από το Cloud",
|
||||
"Remove from Device Only": "Αφαίρεση μόνο από το Device",
|
||||
"Disconnected": "Αποσυνδεδεμένο",
|
||||
"KOReader Sync Settings": "Ρυθμίσεις συγχρονισμού KOReader",
|
||||
"Sync Strategy": "Στρατηγική συγχρονισμού",
|
||||
"Ask on conflict": "Ρώτησε σε περίπτωση σύγκρουσης",
|
||||
"Always use latest": "Χρησιμοποίησε πάντα την τελευταία έκδοση",
|
||||
"Send changes only": "Αποστολή μόνο αλλαγών",
|
||||
"Receive changes only": "Λήψη μόνο αλλαγών",
|
||||
"Disabled": "Απενεργοποιημένο",
|
||||
"Checksum Method": "Μέθοδος ελέγχου",
|
||||
"File Content (recommended)": "Περιεχόμενο αρχείου (συνιστάται)",
|
||||
"File Name": "Όνομα αρχείου",
|
||||
"Device Name": "Όνομα συσκευής",
|
||||
"Sync Tolerance": "Ανοχή συγχρονισμού",
|
||||
"Connect to your KOReader Sync server.": "Συνδεθείτε στον διακομιστή συγχρονισμού KOReader.",
|
||||
"Server URL": "Διεύθυνση URL διακομιστή",
|
||||
"Username": "Όνομα χρήστη",
|
||||
"Your Username": "Το όνομα χρήστη σας",
|
||||
"Password": "Κωδικός πρόσβασης",
|
||||
"Connect": "Σύνδεση",
|
||||
"KOReader Sync": "Συγχρονισμός KOReader",
|
||||
"Sync Conflict": "Σύγκρουση συγχρονισμού",
|
||||
"Sync reading progress from \"{{deviceName}}\"?": "Συγχρονίστε την πρόοδο ανάγνωσης από το \"{{deviceName}}\";",
|
||||
"another device": "άλλη συσκευή",
|
||||
"Local Progress": "Τοπική πρόοδος",
|
||||
"Remote Progress": "Απομακρυσμένη πρόοδος",
|
||||
"Page {{page}} of {{total}} ({{percentage}}%)": "Σελίδα {{page}} από {{total}} ({{percentage}}%)",
|
||||
"Current position": "Τρέχουσα θέση",
|
||||
"Approximately page {{page}} of {{total}} ({{percentage}}%)": "Περίπου σελίδα {{page}} από {{total}} ({{percentage}}%)",
|
||||
"Approximately {{percentage}}%": "Περίπου {{percentage}}%",
|
||||
"Failed to connect": "Αποτυχία σύνδεσης",
|
||||
"Sync Server Connected": "Ο διακομιστής συγχρονισμού είναι συνδεδεμένος",
|
||||
"Precision: {{precision}} digits after the decimal": "Ακρίβεια: {{precision}} ψηφία μετά την υποδιαστολή",
|
||||
"Sync as {{userDisplayName}}": "Συγχρονισμός ως {{userDisplayName}}"
|
||||
}
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
{
|
||||
"LXGW WenKai GB Screen": "LXGW WenKai SC",
|
||||
"LXGW WenKai TC": "LXGW WenKai TC",
|
||||
"Source Han Serif CN VF": "Source Han Serif",
|
||||
"Huiwen-mincho": "Huiwen Mincho",
|
||||
"KingHwa_OldSong": "KingHwa Song",
|
||||
"Are you sure to delete {{count}} selected book(s)?_one": "Are you sure to delete {{count}} selected book?",
|
||||
"Are you sure to delete {{count}} selected book(s)?_other": "Are you sure to delete {{count}} selected books?",
|
||||
"Search in {{count}} Book(s)..._one": "Search in {{count}} book...",
|
||||
"Search in {{count}} Book(s)..._other": "Search in {{count}} books..."
|
||||
"Search in {{count}} Book(s)..._other": "Search in {{count}} books...",
|
||||
"{{count}} pages left in chapter_one": "{{count}} page left in chapter",
|
||||
"{{count}} pages left in chapter_other": "{{count}} pages left in chapter"
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
"Confirm Deletion": "Confirmar eliminación",
|
||||
"Copied to notebook": "Copiado al cuaderno",
|
||||
"Copy": "Copiar",
|
||||
"Custom CSS": "CSS personalizado",
|
||||
"Dark Mode": "Modo oscuro",
|
||||
"Default": "Predeterminado",
|
||||
"Default Font": "Fuente predeterminada",
|
||||
@@ -28,7 +27,6 @@
|
||||
"Dictionary": "Diccionario",
|
||||
"Download Readest": "Descargar Readest",
|
||||
"Edit": "Editar",
|
||||
"Enter your custom CSS here...": "Introduce tu CSS personalizado aquí...",
|
||||
"Excerpts": "Extractos",
|
||||
"Failed to import book(s): {{filenames}}": "Error al importar libro(s): {{filenames}}",
|
||||
"Fast": "Rápido",
|
||||
@@ -48,7 +46,6 @@
|
||||
"Horizontal Direction": "Dirección horizontal",
|
||||
"Hyphenation": "Guionización",
|
||||
"Import Books": "Importar libros",
|
||||
"Language:": "Idioma:",
|
||||
"Layout": "Diseño",
|
||||
"Light Mode": "Modo claro",
|
||||
"Loading...": "Cargando...",
|
||||
@@ -70,9 +67,38 @@
|
||||
"Paging Animation": "Animación de paginación",
|
||||
"Paragraph": "Párrafo",
|
||||
"Parallel Read": "Lectura paralela",
|
||||
"Published:": "Publicado:",
|
||||
"Publisher:": "Editorial:",
|
||||
"Published": "Publicado",
|
||||
"Publisher": "Editorial",
|
||||
"KOReader Sync": "Sincronización con KOReader",
|
||||
"KOReader Sync Settings": "Ajustes de Sincronización con KOReader",
|
||||
"Your Username": "Tu nombre de usuario",
|
||||
"Connect to your KOReader Sync server.": "Conéctate a tu servidor de KOReader Sync.",
|
||||
"Server URL": "URL del Servidor",
|
||||
"Username": "Usuario",
|
||||
"Password": "Contraseña",
|
||||
"Connect": "Conectar",
|
||||
"Disconnected": "Desconectado",
|
||||
"Sync Strategy": "Estrategia de sincronización",
|
||||
"Ask on conflict": "Preguntar en caso de conflicto",
|
||||
"Always use latest": "Usar siempre el más reciente",
|
||||
"Send changes only": "Solo enviar cambios",
|
||||
"Receive changes only": "Solo recibir cambios",
|
||||
"Disabled": "Desactivado",
|
||||
"Checksum Method": "Método de identificación",
|
||||
"File Content (recommended)": "Contenido del archivo (recomendado)",
|
||||
"File Name": "Nombre del archivo",
|
||||
"Device Name": "Nombre del dispositivo",
|
||||
"Sync Tolerance": "Tolerancia de sincronización",
|
||||
"Reading Progress Synced": "Progreso de lectura sincronizado",
|
||||
"Sync Conflict": "Conflicto de sincronización",
|
||||
"Sync reading progress from \"{{deviceName}}\"?": "¿Quieres sincronizar el progreso de lectura desde el dispositivo \"{{deviceName}}\"?",
|
||||
"Local Progress": "Progreso local",
|
||||
"Remote Progress": "Progreso remoto",
|
||||
"Page {{page}} of {{total}} ({{percentage}}%)": "Página {{page}} de {{total}} ({{percentage}}%)",
|
||||
"Approximately page {{page}} of {{total}} ({{percentage}}%)": "Aproximadamente página {{page}} de {{total}} ({{percentage}}%)",
|
||||
"Current position": "Posición actual",
|
||||
"Approximately {{percentage}}%": "Aproximadamente {{percentage}}%",
|
||||
"another device": "otro dispositivo",
|
||||
"Reload Page": "Recargar página",
|
||||
"Reveal in File Explorer": "Mostrar en el Explorador de archivos",
|
||||
"Reveal in Finder": "Mostrar en Finder",
|
||||
@@ -96,16 +122,15 @@
|
||||
"Slow": "Lento",
|
||||
"Solarized": "Solarizado",
|
||||
"Speak": "Leer",
|
||||
"Subjects:": "Temas:",
|
||||
"Subjects": "Temas",
|
||||
"System Fonts": "Fuentes del sistema",
|
||||
"TTS not supported in this device": "TTS no es compatible con este dispositivo",
|
||||
"Theme Color": "Color del tema",
|
||||
"Theme Mode": "Modo del tema",
|
||||
"Translate": "Traducir",
|
||||
"Translated Text": "Texto traducido",
|
||||
"Unknown": "Desconocido",
|
||||
"Untitled": "Sin título",
|
||||
"Updated:": "Actualizado:",
|
||||
"Updated": "Actualizado",
|
||||
"Version {{version}}": "Versión {{version}}",
|
||||
"Vertical Direction": "Dirección vertical",
|
||||
"Welcome to your library. You can import your books here and read them anytime.": "Bienvenido a tu biblioteca. Puedes importar tus libros aquí y leerlos en cualquier momento.",
|
||||
@@ -114,8 +139,6 @@
|
||||
"Your Library": "Tu biblioteca",
|
||||
"TTS not supported for PDF": "TTS no es compatible con PDF",
|
||||
"Override Book Font": "Sobrescribir fuente del libro",
|
||||
"Vertical Margins (px)": "Márgenes verticales (px)",
|
||||
"Horizontal Margins (%)": "Márgenes horizontales (%)",
|
||||
"Apply to All Books": "Aplicar a todos los libros",
|
||||
"Apply to This Book": "Aplicar a este libro",
|
||||
"Unable to fetch the translation. Try again later.": "No se puede obtener la traducción. Inténtalo de nuevo más tarde.",
|
||||
@@ -130,8 +153,6 @@
|
||||
"Failed to download book: {{title}}": "Error al descargar libro: {{title}}",
|
||||
"Upload Book": "Subir libro",
|
||||
"Auto Upload Books to Cloud": "Subir libros automáticamente a la nube",
|
||||
"{{percentage}}% of Cloud Storage Used.": "{{percentage}}% del almacenamiento en la nube utilizado.",
|
||||
"Storage": "Almacenamiento",
|
||||
"Book deleted: {{title}}": "Libro eliminado: {{title}}",
|
||||
"Failed to delete book: {{title}}": "Error al eliminar libro: {{title}}",
|
||||
"Check Updates on Start": "Comprobar actualizaciones al iniciar",
|
||||
@@ -163,7 +184,7 @@
|
||||
"Create a Password": "Crear una contraseña",
|
||||
"Sign up": "Registrarse",
|
||||
"Signing up...": "Registrándose...",
|
||||
"Don’t have an account? Sign up": "¿No tienes una cuenta? Regístrate",
|
||||
"Don't have an account? Sign up": "¿No tienes una cuenta? Regístrate",
|
||||
"Check your email for the confirmation link": "Revisa tu correo electrónico para el enlace de confirmación",
|
||||
"Signing in ...": "Iniciando sesión ...",
|
||||
"Send a magic link email": "Enviar un enlace mágico por correo",
|
||||
@@ -187,35 +208,13 @@
|
||||
"Sign in with GitHub": "Iniciar sesión con GitHub",
|
||||
"Account": "Cuenta",
|
||||
"Failed to delete user. Please try again later.": "Error al eliminar usuario. Por favor, inténtelo de nuevo más tarde.",
|
||||
"Unlimited Offline/Online Reading": "Lectura sin límites en línea/sin conexión",
|
||||
"Unlimited Cloud Sync Devices": "Sincronización ilimitada de dispositivos en la nube",
|
||||
"Essential Text-to-Speech Voices": "Voces esenciales de texto a voz",
|
||||
"DeepL Free Access": "Acceso gratuito a DeepL",
|
||||
"Community Support": "Soporte comunitario",
|
||||
"500 MB Cloud Sync Space": "500 MB de espacio de sincronización en la nube",
|
||||
"Includes All Free Tier Benefits": "Incluye todos los beneficios del nivel gratuito",
|
||||
"AI Summaries": "Resúmenes con IA",
|
||||
"AI Translations": "Traducciones con IA",
|
||||
"Priority Support": "Soporte prioritario",
|
||||
"DeepL Pro Access": "Acceso a DeepL Pro",
|
||||
"Expanded Text-to-Speech Voices": "Voces ampliadas de texto a voz",
|
||||
"2000 MB Cloud Sync Space": "2000 MB de espacio de sincronización en la nube",
|
||||
"Includes All Plus Tier Benefits": "Incluye todos los beneficios del nivel Plus",
|
||||
"Unlimited AI Summaries": "Resúmenes ilimitados con IA",
|
||||
"Unlimited AI Translations": "Traducciones ilimitadas con IA",
|
||||
"Unlimited DeepL Pro Access": "Acceso ilimitado a DeepL Pro",
|
||||
"Advanced AI Tools": "Herramientas avanzadas de IA",
|
||||
"Early Feature Access": "Acceso anticipado a funciones",
|
||||
"10 GB Cloud Sync Space": "10 GB de espacio de sincronización en la nube",
|
||||
"Loading profile...": "Cargando perfil...",
|
||||
"Features": "Características",
|
||||
"Delete Account": "Eliminar cuenta",
|
||||
"Delete Your Account?": "¿Eliminar tu cuenta?",
|
||||
"This action cannot be undone. All your data in the cloud will be permanently deleted.": "Esta acción no se puede deshacer. Todos tus datos en la nube se eliminarán permanentemente.",
|
||||
"Delete Permanently": "Eliminar permanentemente",
|
||||
"Free Tier": "Nivel gratuito",
|
||||
"Plus Tier": "Nivel Plus",
|
||||
"Pro Tier": "Nivel Pro",
|
||||
"RTL Direction": "Dirección RTL",
|
||||
"Maximum Column Height": "Altura máxima de columna",
|
||||
"Maximum Column Width": "Ancho máximo de columna",
|
||||
@@ -266,13 +265,13 @@
|
||||
"Clear Search": "Limpiar búsqueda",
|
||||
"Header & Footer": "Encabezado y pie de página",
|
||||
"Apply also in Scrolled Mode": "Aplicar también en modo desplazamiento",
|
||||
"A new version of Readest is Available!": "¡Una nueva versión de Readest está disponible!",
|
||||
"A new version of Readest is available!": "¡Una nueva versión de Readest está disponible!",
|
||||
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} está disponible (versión instalada {{currentVersion}}).",
|
||||
"Download and install now?": "Descargar e instalar ahora?",
|
||||
"Downloading {{downloaded}} of {{contentLength}}": "Descargando {{downloaded}} de {{contentLength}}",
|
||||
"Download finished": "Descarga finalizada",
|
||||
"DOWNLOAD & INSTALL": "DESCARGAR E INSTALAR",
|
||||
"Changelog:": "Notas de la versión:",
|
||||
"Changelog": "Notas de la versión",
|
||||
"Software Update": "Actualización de software",
|
||||
"Title": "Título",
|
||||
"Date Read": "Fecha de lectura",
|
||||
@@ -281,10 +280,9 @@
|
||||
"Ascending": "Ascendente",
|
||||
"Descending": "Descendente",
|
||||
"Sort by...": "Ordenar por...",
|
||||
"Added:": "Agregado:",
|
||||
"Format:": "Formato:",
|
||||
"Added": "Agregado",
|
||||
"GuanKiapTsingKhai-T": "GuanKiapTsingKhai-T",
|
||||
"Description:": "Descripción:",
|
||||
"Description": "Descripción",
|
||||
"No description available": "No hay descripción disponible",
|
||||
"List": "Lista",
|
||||
"Grid": "Cuadrícula",
|
||||
@@ -301,7 +299,7 @@
|
||||
"Checking for updates...": "Comprobando actualizaciones...",
|
||||
"Error checking for updates": "Error al comprobar actualizaciones",
|
||||
"Details": "Detalles",
|
||||
"File Size:": "Peso del archivo:",
|
||||
"File Size": "Peso del archivo",
|
||||
"Auto Detect": "Detección automática",
|
||||
"Next Section": "Siguiente sección",
|
||||
"Previous Section": "Sección anterior",
|
||||
@@ -344,14 +342,12 @@
|
||||
"Synced at {{time}}": "Sincronizado a las {{time}}",
|
||||
"Never synced": "Nunca sincronizado",
|
||||
"Show Remaining Time": "Mostrar tiempo restante",
|
||||
"Show Page Number": "Mostrar número de página",
|
||||
"{{time}} min left in chapter": "{{time}} min restantes en el capítulo",
|
||||
"Override Book Color": "Sobre escribir color del libro",
|
||||
"Login Required": "Iniciar sesión",
|
||||
"Quota Exceeded": "Límite superado",
|
||||
"{{percentage}}% of Daily Translation Characters Used.": "{{percentage}}% de caracteres de traducción diarios utilizados.",
|
||||
"Translation Characters": "Caracteres de traducción",
|
||||
"Daily translation quota reached. Select another translate service to proceed.": "Cuota diaria de traducción alcanzada. Selecciona otro servicio de traducción para continuar.",
|
||||
"{{engine}}: {{count}} voices_one": "{{engine}}: {{count}} voz",
|
||||
"{{engine}}: {{count}} voices_many": "{{engine}}: {{count}} voces",
|
||||
"{{engine}}: {{count}} voices_other": "{{engine}}: {{count}} voces",
|
||||
@@ -362,5 +358,155 @@
|
||||
"Contact Support": "Contactar con soporte",
|
||||
"Code Highlighting": "Resaltado de código",
|
||||
"Enable Highlighting": "Activar resaltado",
|
||||
"Code Language": "Lenguaje de código"
|
||||
"Code Language": "Lenguaje de código",
|
||||
"Top Margin (px)": "Margen superior (px)",
|
||||
"Bottom Margin (px)": "Margen inferior (px)",
|
||||
"Right Margin (px)": "Margen derecho (px)",
|
||||
"Left Margin (px)": "Margen izquierdo (px)",
|
||||
"Column Gap (%)": "Espacio entre columnas (%)",
|
||||
"Always Show Status Bar": "Mostrar siempre la barra de estado",
|
||||
"Translation Not Available": "Traducción no disponible",
|
||||
"Custom Content CSS": "CSS del contenido",
|
||||
"Enter CSS for book content styling...": "Introduce CSS para el contenido del libro...",
|
||||
"Custom Reader UI CSS": "CSS de la interfaz",
|
||||
"Enter CSS for reader interface styling...": "Introduce CSS para la interfaz de lectura...",
|
||||
"Crop": "Recortar",
|
||||
"Book Covers": "Portadas de libros",
|
||||
"Fit": "Encajar",
|
||||
"Reset {{settings}}": "Restablecer {{settings}}",
|
||||
"Reset Settings": "Restablecer configuración",
|
||||
"{{count}} pages left in chapter_one": "{{count}} página restante en el capítulo",
|
||||
"{{count}} pages left in chapter_many": "{{count}} páginas restantes en el capítulo",
|
||||
"{{count}} pages left in chapter_other": "{{count}} páginas restantes en el capítulo",
|
||||
"Show Remaining Pages": "Mostrar resto",
|
||||
"Source Han Serif CN VF": "Source Han Serif",
|
||||
"Huiwen-mincho": "Huiwen Mincho",
|
||||
"KingHwa_OldSong": "KingHwa Song",
|
||||
"Manage Subscription": "Gestionar suscripción",
|
||||
"Coming Soon": "Próximamente",
|
||||
"Upgrade to {{plan}}": "Actualizar a {{plan}}",
|
||||
"Upgrade to Plus or Pro": "Actualizar a Plus o Pro",
|
||||
"Current Plan": "Plan actual",
|
||||
"Plan Limits": "Límites del plan",
|
||||
"Processing your payment...": "Procesando tu pago...",
|
||||
"Please wait while we confirm your subscription.": "Espera mientras confirmamos tu suscripción.",
|
||||
"Payment Processing": "Procesando pago",
|
||||
"Payment Failed": "El pago ha fallado",
|
||||
"We couldn't process your subscription. Please try again or contact support if the issue persists.": "No pudimos procesar tu suscripción. Inténtalo de nuevo o contacta con soporte si el problema persiste.",
|
||||
"Back to Profile": "Volver al perfil",
|
||||
"Subscription Successful!": "¡Suscripción exitosa!",
|
||||
"Thank you for your subscription! Your payment has been processed successfully.": "¡Gracias por tu suscripción! Tu pago se ha procesado correctamente.",
|
||||
"Email:": "Correo electrónico:",
|
||||
"Plan:": "Plan:",
|
||||
"Amount:": "Importe:",
|
||||
"Subscription ID:": "ID de suscripción:",
|
||||
"Go to Library": "Ir a la biblioteca",
|
||||
"Need help? Contact our support team at support@readest.com": "¿Necesitas ayuda? Contacta con nuestro equipo de soporte en support@readest.com",
|
||||
"Free Plan": "Plan gratuito",
|
||||
"month": "mes",
|
||||
"AI Translations (per day)": "Traducciones con IA (por día)",
|
||||
"Plus Plan": "Plan Plus",
|
||||
"Includes All Free Plan Benefits": "Incluye todos los beneficios del plan gratuito",
|
||||
"Pro Plan": "Plan Pro",
|
||||
"More AI Translations": "Más traducciones con IA",
|
||||
"Your payment is being processed. This usually takes a few moments.": "Tu pago se está procesando. Normalmente tarda unos momentos.",
|
||||
"Complete Your Subscription": "Completa tu suscripción",
|
||||
"{{percentage}}% of Cloud Sync Space Used.": "{{percentage}}% del espacio de sincronización en la nube utilizado.",
|
||||
"Cloud Sync Storage": "Almacenamiento en la nube",
|
||||
"Disable": "Desactivar",
|
||||
"Enable": "Activar",
|
||||
"Upgrade to Readest Premium": "Actualizar a Readest Premium",
|
||||
"Show Source Text": "Mostrar texto fuente",
|
||||
"Cross-Platform Sync": "Sincronización multiplataforma",
|
||||
"Seamlessly sync your library, progress, highlights, and notes across all your devices—never lose your place again.": "Sincroniza tu biblioteca, progreso y notas en todos tus dispositivos.",
|
||||
"Customizable Reading": "Lectura personalizable",
|
||||
"Personalize every detail with adjustable fonts, layouts, themes, and advanced display settings for the perfect reading experience.": "Ajusta fuentes, diseño y temas a tu gusto.",
|
||||
"AI Read Aloud": "Lectura en voz alta con IA",
|
||||
"Enjoy hands-free reading with natural-sounding AI voices that bring your books to life.": "Escucha tus libros con voces IA naturales.",
|
||||
"AI Translations": "Traducciones con IA",
|
||||
"Translate any text instantly with the power of Google, Azure, or DeepL—understand content in any language.": "Traduce textos al instante con Google, Azure o DeepL.",
|
||||
"Connect with fellow readers and get help fast in our friendly community channels.": "Únete a la comunidad y recibe ayuda rápidamente.",
|
||||
"Unlimited AI Read Aloud Hours": "Lectura IA ilimitada",
|
||||
"Listen without limits—convert as much text as you like into immersive audio.": "Convierte texto en audio sin límites.",
|
||||
"Unlock enhanced translation capabilities with more daily usage and advanced options.": "Más traducciones y funciones avanzadas.",
|
||||
"DeepL Pro Access": "Acceso a DeepL Pro",
|
||||
"Enjoy faster responses and dedicated assistance whenever you need help.": "Soporte rápido y prioritario.",
|
||||
"Daily translation quota reached. Upgrade your plan to continue using AI translations.": "Has alcanzado tu límite diario. Mejora tu plan para más traducciones.",
|
||||
"Includes All Plus Plan Benefits": "Incluye Todos los Beneficios del Plan Plus",
|
||||
"Early Feature Access": "Acceso Anticipado a Funciones",
|
||||
"Be the first to explore new features, updates, and innovations before anyone else.": "Explora primero nuevas funciones, actualizaciones e innovaciones antes que nadie.",
|
||||
"Advanced AI Tools": "Herramientas de IA Avanzadas",
|
||||
"Harness powerful AI tools for smarter reading, translation, and content discovery.": "Aprovecha herramientas de IA potentes para lectura inteligente, traducción y descubrimiento de contenido.",
|
||||
"Translate up to 100,000 characters daily with the most accurate translation engine available.": "Traduce hasta 100,000 caracteres diarios con el motor de traducción más preciso disponible.",
|
||||
"Translate up to 500,000 characters daily with the most accurate translation engine available.": "Traduce hasta 500,000 caracteres diarios con el motor de traducción más preciso disponible.",
|
||||
"Securely store and access your entire reading collection with up to 5 GB of cloud storage.": "Almacena y accede de forma segura a toda tu colección de lectura con hasta 5 GB de almacenamiento en la nube.",
|
||||
"Securely store and access your entire reading collection with up to 20 GB of cloud storage.": "Almacena y accede de forma segura a toda tu colección de lectura con hasta 20 GB de almacenamiento en la nube.",
|
||||
"Deleted cloud backup of the book: {{title}}": "Respaldo en la nube del libro eliminado: {{title}}",
|
||||
"Are you sure to delete the cloud backup of the selected book?": "¿Estás seguro de que deseas eliminar el respaldo en la nube del libro seleccionado?",
|
||||
"What's New in Readest": "¿Qué hay de nuevo en Readest",
|
||||
"Enter book title": "Ingresa el título del libro",
|
||||
"Subtitle": "Subtítulo",
|
||||
"Enter book subtitle": "Ingresa el subtítulo del libro",
|
||||
"Enter author name": "Ingresa el nombre del autor",
|
||||
"Series": "Serie",
|
||||
"Enter series name": "Ingresa el nombre de la serie",
|
||||
"Series Index": "Número de serie",
|
||||
"Enter series index": "Ingresa el número de serie",
|
||||
"Total in Series": "Total en la serie",
|
||||
"Enter total books in series": "Ingresa el total de libros en la serie",
|
||||
"Enter publisher": "Ingresa la editorial",
|
||||
"Publication Date": "Fecha de publicación",
|
||||
"Identifier": "Identificador",
|
||||
"Enter book description": "Ingresa la descripción del libro",
|
||||
"Change cover image": "Cambiar imagen de portada",
|
||||
"Replace": "Reemplazar",
|
||||
"Unlock cover": "Desbloquear portada",
|
||||
"Lock cover": "Bloquear portada",
|
||||
"Auto-Retrieve Metadata": "Recuperar metadatos automáticamente",
|
||||
"Auto-Retrieve": "Recuperar automáticamente",
|
||||
"Unlock all fields": "Desbloquear todos los campos",
|
||||
"Unlock All": "Desbloquear todo",
|
||||
"Lock all fields": "Bloquear todos los campos",
|
||||
"Lock All": "Bloquear todo",
|
||||
"Reset": "Restablecer",
|
||||
"Edit Metadata": "Editar metadatos",
|
||||
"Locked": "Bloqueado",
|
||||
"Select Metadata Source": "Seleccionar fuente de metadatos",
|
||||
"Keep manual input": "Mantener entrada manual",
|
||||
"Google Books": "Google Books",
|
||||
"Open Library": "Open Library",
|
||||
"Fiction, Science, History": "Ficción, Ciencia, Historia",
|
||||
"Select Cover Image": "Seleccionar imagen de portada",
|
||||
"Open Book in New Window": "Abre el libro en una nueva ventana",
|
||||
"Voices for {{lang}}": "Voces para {{lang}}",
|
||||
"Yandex Translate": "Yandex Translate",
|
||||
"YYYY or YYYY-MM-DD": "YYYY o YYYY-MM-DD",
|
||||
"Restore Purchase": "Restaurar compra",
|
||||
"No purchases found to restore.": "No se encontraron compras para restaurar.",
|
||||
"Failed to restore purchases.": "Error al restaurar compras.",
|
||||
"Failed to manage subscription.": "Error al gestionar la suscripción.",
|
||||
"Failed to load subscription plans.": "Error al cargar los planes de suscripción.",
|
||||
"year": "año",
|
||||
"Failed to create checkout session": "Error al crear la sesión de pago",
|
||||
"Storage": "Almacenamiento",
|
||||
"Terms of Service": "Condiciones del servicio",
|
||||
"Privacy Policy": "Política de privacidad",
|
||||
"Disable Double Click": "Deshabilitar Doble Clic",
|
||||
"TTS not supported for this document": "TTS no es compatible con este documento.",
|
||||
"Reset Password": "Restablecer contraseña",
|
||||
"Reading Progress Style": "Estilo de progreso de lectura",
|
||||
"Percentage": "Porcentaje",
|
||||
"Show Reading Progress": "Mostrar progreso de lectura",
|
||||
"Page Number": "Número de página",
|
||||
"Deleted local copy of the book: {{title}}": "Se eliminó la copia local del libro: {{title}}",
|
||||
"Failed to delete cloud backup of the book: {{title}}": "Error al eliminar la copia de seguridad en la nube del libro: {{title}}",
|
||||
"Failed to delete local copy of the book: {{title}}": "Error al eliminar la copia local del libro: {{title}}",
|
||||
"Are you sure to delete the local copy of the selected book?": "¿Está seguro de que desea eliminar la copia local del libro seleccionado?",
|
||||
"Remove from Cloud & Device": "Eliminar de la nube y del dispositivo",
|
||||
"Remove from Cloud Only": "Eliminar solo de la nube",
|
||||
"Remove from Device Only": "Eliminar solo del dispositivo",
|
||||
"Failed to connect": "Error al conectar",
|
||||
"Sync Server Connected": "Servidor de sincronización conectado",
|
||||
"Precision: {{precision}} digits after the decimal": "Precisión: {{precision}} dígitos después del decimal",
|
||||
"Sync as {{userDisplayName}}": "Sincronizar como {{userDisplayName}}"
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
"Confirm Deletion": "Confirmer la suppression",
|
||||
"Copied to notebook": "Copié dans le carnet de notes",
|
||||
"Copy": "Copier",
|
||||
"Custom CSS": "CSS personnalisé",
|
||||
"Dark Mode": "Mode sombre",
|
||||
"Default": "Par défaut",
|
||||
"Default Font": "Police par défaut",
|
||||
@@ -28,7 +27,6 @@
|
||||
"Dictionary": "Dictionnaire",
|
||||
"Download Readest": "Télécharger Readest",
|
||||
"Edit": "Modifier",
|
||||
"Enter your custom CSS here...": "Saisissez votre CSS personnalisé ici...",
|
||||
"Excerpts": "Extraits",
|
||||
"Failed to import book(s): {{filenames}}": "Échec de l'importation du/des livre(s) : {{filenames}}",
|
||||
"Fast": "Rapide",
|
||||
@@ -48,7 +46,6 @@
|
||||
"Horizontal Direction": "Direction horizontale",
|
||||
"Hyphenation": "Césure",
|
||||
"Import Books": "Importer des livres",
|
||||
"Language:": "Langue :",
|
||||
"Layout": "Mise en page",
|
||||
"Light Mode": "Mode clair",
|
||||
"Loading...": "Chargement...",
|
||||
@@ -70,8 +67,8 @@
|
||||
"Paging Animation": "Animation de page",
|
||||
"Paragraph": "Paragraphe",
|
||||
"Parallel Read": "Lecture parallèle",
|
||||
"Published:": "Publié :",
|
||||
"Publisher:": "Éditeur :",
|
||||
"Published": "Publié",
|
||||
"Publisher": "Éditeur",
|
||||
"Reading Progress Synced": "Progression de lecture synchronisée",
|
||||
"Reload Page": "Recharger la page",
|
||||
"Reveal in File Explorer": "Afficher dans l'explorateur de fichiers",
|
||||
@@ -96,16 +93,15 @@
|
||||
"Slow": "Lent",
|
||||
"Solarized": "Solarisé",
|
||||
"Speak": "Lire",
|
||||
"Subjects:": "Sujets :",
|
||||
"Subjects": "Sujets",
|
||||
"System Fonts": "Polices système",
|
||||
"TTS not supported in this device": "La synthèse vocale n'est pas prise en charge sur cet appareil",
|
||||
"Theme Color": "Couleur du thème",
|
||||
"Theme Mode": "Mode du thème",
|
||||
"Translate": "Traduire",
|
||||
"Translated Text": "Texte traduit",
|
||||
"Unknown": "Inconnu",
|
||||
"Untitled": "Sans titre",
|
||||
"Updated:": "Mis à jour :",
|
||||
"Updated": "Mis à jour",
|
||||
"Version {{version}}": "Version {{version}}",
|
||||
"Vertical Direction": "Direction verticale",
|
||||
"Welcome to your library. You can import your books here and read them anytime.": "Bienvenue dans votre bibliothèque. Vous pouvez importer vos livres ici et les lire à tout moment.",
|
||||
@@ -114,8 +110,6 @@
|
||||
"Your Library": "Votre bibliothèque",
|
||||
"TTS not supported for PDF": "La synthèse vocale n'est pas prise en charge pour les fichiers PDF",
|
||||
"Override Book Font": "Remplacer la police du livre",
|
||||
"Vertical Margins (px)": "Marges verticales (px)",
|
||||
"Horizontal Margins (%)": "Marges horizontales (%)",
|
||||
"Apply to All Books": "Appliquer à tous les livres",
|
||||
"Apply to This Book": "Appliquer à ce livre",
|
||||
"Unable to fetch the translation. Try again later.": "Impossible de récupérer la traduction. Réessayez plus tard.",
|
||||
@@ -130,8 +124,6 @@
|
||||
"Failed to download book: {{title}}": "Échec du téléchargement du livre : {{title}}",
|
||||
"Upload Book": "Télécharger un livre",
|
||||
"Auto Upload Books to Cloud": "Télécharger automatiquement les livres dans le cloud",
|
||||
"{{percentage}}% of Cloud Storage Used.": "{{percentage}}% de l'espace de stockage dans le cloud utilisé.",
|
||||
"Storage": "Stockage",
|
||||
"Book deleted: {{title}}": "Livre supprimé : {{title}}",
|
||||
"Failed to delete book: {{title}}": "Échec de la suppression du livre : {{title}}",
|
||||
"Check Updates on Start": "Vérifier les mises à jour au démarrage",
|
||||
@@ -163,7 +155,6 @@
|
||||
"Create a Password": "Créer un mot de passe",
|
||||
"Sign up": "S'inscrire",
|
||||
"Signing up...": "Inscription en cours...",
|
||||
"Don’t have an account? Sign up": "Vous n'avez pas de compte ? Inscrivez-vous",
|
||||
"Check your email for the confirmation link": "Vérifiez votre e-mail pour le lien de confirmation",
|
||||
"Signing in ...": "Connexion en cours ...",
|
||||
"Send a magic link email": "Envoyer un e-mail avec un lien magique",
|
||||
@@ -187,35 +178,13 @@
|
||||
"Sign in with GitHub": "Se connecter avec GitHub",
|
||||
"Account": "Compte",
|
||||
"Failed to delete user. Please try again later.": "Échec de la suppression de l'utilisateur. Veuillez réessayer plus tard.",
|
||||
"Unlimited Offline/Online Reading": "Lecture illimitée hors ligne/en ligne",
|
||||
"Unlimited Cloud Sync Devices": "Synchronisation cloud sur appareils illimitée",
|
||||
"Essential Text-to-Speech Voices": "Voix de synthèse vocale essentielles",
|
||||
"DeepL Free Access": "Accès gratuit à DeepL",
|
||||
"Community Support": "Support communautaire",
|
||||
"500 MB Cloud Sync Space": "500 Mo d'espace de synchronisation cloud",
|
||||
"Includes All Free Tier Benefits": "Inclut tous les avantages du niveau gratuit",
|
||||
"AI Summaries": "Résumés par IA",
|
||||
"AI Translations": "Traductions par IA",
|
||||
"Priority Support": "Support prioritaire",
|
||||
"DeepL Pro Access": "Accès à DeepL Pro",
|
||||
"Expanded Text-to-Speech Voices": "Voix de synthèse vocale étendues",
|
||||
"2000 MB Cloud Sync Space": "2000 Mo d'espace de synchronisation cloud",
|
||||
"Includes All Plus Tier Benefits": "Inclut tous les avantages du niveau Plus",
|
||||
"Unlimited AI Summaries": "Résumés par IA illimités",
|
||||
"Unlimited AI Translations": "Traductions par IA illimitées",
|
||||
"Unlimited DeepL Pro Access": "Accès illimité à DeepL Pro",
|
||||
"Advanced AI Tools": "Outils IA avancés",
|
||||
"Early Feature Access": "Accès anticipé aux fonctionnalités",
|
||||
"10 GB Cloud Sync Space": "10 Go d'espace de synchronisation cloud",
|
||||
"Loading profile...": "Chargement du profil...",
|
||||
"Features": "Fonctionnalités",
|
||||
"Delete Account": "Supprimer le compte",
|
||||
"Delete Your Account?": "Supprimer votre compte ?",
|
||||
"This action cannot be undone. All your data in the cloud will be permanently deleted.": "Cette action est irréversible. Toutes vos données dans le cloud seront définitivement supprimées.",
|
||||
"Delete Permanently": "Supprimer définitivement",
|
||||
"Free Tier": "Niveau gratuit",
|
||||
"Plus Tier": "Niveau Plus",
|
||||
"Pro Tier": "Niveau Pro",
|
||||
"RTL Direction": "Direction RTL",
|
||||
"Maximum Column Height": "Hauteur maximale de colonne",
|
||||
"Maximum Column Width": "Largeur maximale de colonne",
|
||||
@@ -266,13 +235,13 @@
|
||||
"Clear Search": "Effacer la recherche",
|
||||
"Header & Footer": "En-tête et pied de page",
|
||||
"Apply also in Scrolled Mode": "Appliquer également en mode défilement",
|
||||
"A new version of Readest is Available!": "Avis de mise à jour",
|
||||
"A new version of Readest is available!": "Avis de mise à jour",
|
||||
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} est disponible (version installée {{currentVersion}}).",
|
||||
"Download and install now?": "Télécharger et installer maintenant ?",
|
||||
"Downloading {{downloaded}} of {{contentLength}}": "Téléchargement {{downloaded}} sur {{contentLength}}",
|
||||
"Download finished": "Téléchargement terminé",
|
||||
"DOWNLOAD & INSTALL": "TÉLÉCHARGER ET INSTALLER",
|
||||
"Changelog:": "Journal des modifications :",
|
||||
"Changelog": "Journal des modifications",
|
||||
"Software Update": "Mise à jour logicielle",
|
||||
"Title": "Titre",
|
||||
"Date Read": "Date de lecture",
|
||||
@@ -281,10 +250,9 @@
|
||||
"Ascending": "Ascendant",
|
||||
"Descending": "Descendant",
|
||||
"Sort by...": "Trier par...",
|
||||
"Added:": "Ajouté :",
|
||||
"Format:": "Format :",
|
||||
"Added": "Ajouté",
|
||||
"GuanKiapTsingKhai-T": "GuanKiapTsingKhai-T",
|
||||
"Description:": "Description :",
|
||||
"Description": "Description",
|
||||
"No description available": "Aucune description disponible",
|
||||
"List": "Liste",
|
||||
"Grid": "Grille",
|
||||
@@ -301,7 +269,7 @@
|
||||
"Checking for updates...": "Vérification des mises à jour...",
|
||||
"Error checking for updates": "Erreur lors de la vérification des mises à jour",
|
||||
"Details": "Détails",
|
||||
"File Size:": "Taille du fichier :",
|
||||
"File Size": "Taille du fichier",
|
||||
"Auto Detect": "Détection automatique",
|
||||
"Next Section": "Section suivante",
|
||||
"Previous Section": "Section précédente",
|
||||
@@ -344,14 +312,12 @@
|
||||
"Synced at {{time}}": "Synchronisé à {{time}}",
|
||||
"Never synced": "Jamais synchronisé",
|
||||
"Show Remaining Time": "Afficher le temps restant",
|
||||
"Show Page Number": "Afficher le numéro de page",
|
||||
"{{time}} min left in chapter": "{{time}} min restant dans le chapitre",
|
||||
"Override Book Color": "Remplacer la couleur du livre",
|
||||
"Login Required": "Connexion requise",
|
||||
"Quota Exceeded": "Quota dépassé",
|
||||
"{{percentage}}% of Daily Translation Characters Used.": "{{percentage}}% des caractères de traduction quotidiens utilisés.",
|
||||
"Translation Characters": "Caractères de traduction",
|
||||
"Daily translation quota reached. Select another translate service to proceed.": "Quota de traduction quotidien atteint. Sélectionnez un autre service de traduction pour continuer.",
|
||||
"{{engine}}: {{count}} voices_one": "{{engine}} : {{count}} voix",
|
||||
"{{engine}}: {{count}} voices_many": "{{engine}} : {{count}} voix",
|
||||
"{{engine}}: {{count}} voices_other": "{{engine}} : {{count}} voix",
|
||||
@@ -362,5 +328,185 @@
|
||||
"Contact Support": "Contacter le support",
|
||||
"Code Highlighting": "Coloration syntaxique",
|
||||
"Enable Highlighting": "Activer la coloration syntaxique",
|
||||
"Code Language": "Langage de programmation"
|
||||
"Code Language": "Langage de programmation",
|
||||
"Top Margin (px)": "Marge supérieure (px)",
|
||||
"Bottom Margin (px)": "Marge inférieure (px)",
|
||||
"Right Margin (px)": "Marge droite (px)",
|
||||
"Left Margin (px)": "Marge gauche (px)",
|
||||
"Column Gap (%)": "Espacement des colonnes (%)",
|
||||
"Always Show Status Bar": "Afficher toujours la barre d'état",
|
||||
"Translation Not Available": "Traduction non disponible",
|
||||
"Custom Content CSS": "CSS du contenu",
|
||||
"Enter CSS for book content styling...": "Saisissez le CSS pour le contenu du livre...",
|
||||
"Custom Reader UI CSS": "CSS de l’interface",
|
||||
"Enter CSS for reader interface styling...": "Saisissez le CSS pour l’interface de lecture...",
|
||||
"Crop": "Rogner",
|
||||
"Book Covers": "Couvertures de livres",
|
||||
"Fit": "Adapter",
|
||||
"Reset {{settings}}": "Réinitialiser {{settings}}",
|
||||
"Reset Settings": "Réinitialiser les paramètres",
|
||||
"{{count}} pages left in chapter_one": "{{count}} page restant dans le chapitre",
|
||||
"{{count}} pages left in chapter_many": "{{count}} pages restantes dans le chapitre",
|
||||
"{{count}} pages left in chapter_other": "{{count}} pages restantes dans le chapitre",
|
||||
"Show Remaining Pages": "Voir restantes",
|
||||
"Source Han Serif CN VF": "Source Han Serif",
|
||||
"Huiwen-mincho": "Huiwen Mincho",
|
||||
"KingHwa_OldSong": "KingHwa Song",
|
||||
"Manage Subscription": "Gérer l’abonnement",
|
||||
"Coming Soon": "Bientôt disponible",
|
||||
"Upgrade to {{plan}}": "Passer à {{plan}}",
|
||||
"Upgrade to Plus or Pro": "Passer à Plus ou Pro",
|
||||
"Current Plan": "Abonnement actuel",
|
||||
"Plan Limits": "Limites de l’abonnement",
|
||||
"Processing your payment...": "Traitement de votre paiement...",
|
||||
"Please wait while we confirm your subscription.": "Veuillez patienter pendant la confirmation de votre abonnement.",
|
||||
"Payment Processing": "Traitement du paiement",
|
||||
"Your payment is being processed. This usually takes a few moments.": "Votre paiement est en cours de traitement. Cela prend généralement quelques instants.",
|
||||
"Payment Failed": "Le paiement a échoué",
|
||||
"We couldn't process your subscription. Please try again or contact support if the issue persists.": "Nous n’avons pas pu traiter votre abonnement. Veuillez réessayer ou contacter le support si le problème persiste.",
|
||||
"Back to Profile": "Retour au profil",
|
||||
"Subscription Successful!": "Abonnement réussi !",
|
||||
"Thank you for your subscription! Your payment has been processed successfully.": "Merci pour votre abonnement ! Votre paiement a été traité avec succès.",
|
||||
"Email:": "E-mail :",
|
||||
"Plan:": "Abonnement :",
|
||||
"Amount:": "Montant :",
|
||||
"Subscription ID:": "ID d’abonnement :",
|
||||
"Go to Library": "Aller à la bibliothèque",
|
||||
"Need help? Contact our support team at support@readest.com": "Besoin d’aide ? Contactez notre équipe à support@readest.com",
|
||||
"Free Plan": "Abonnement gratuit",
|
||||
"month": "mois",
|
||||
"AI Translations (per day)": "Traductions IA (par jour)",
|
||||
"Plus Plan": "Abonnement Plus",
|
||||
"Includes All Free Plan Benefits": "Inclut tous les avantages de l’abonnement gratuit",
|
||||
"Pro Plan": "Abonnement Pro",
|
||||
"More AI Translations": "Plus de traductions IA",
|
||||
"Complete Your Subscription": "Complétez votre abonnement",
|
||||
"{{percentage}}% of Cloud Sync Space Used.": "{{percentage}}% de l’espace de synchronisation cloud utilisé.",
|
||||
"Cloud Sync Storage": "Stockage de synchronisation cloud",
|
||||
"Disable": "Désactiver",
|
||||
"Enable": "Activer",
|
||||
"Upgrade to Readest Premium": "Passer à Readest Premium",
|
||||
"Show Source Text": "Afficher le texte source",
|
||||
"Cross-Platform Sync": "Synchronisation multiplateforme",
|
||||
"Seamlessly sync your library, progress, highlights, and notes across all your devices—never lose your place again.": "Synchronisez bibliothèque, progrès et notes sur tous vos appareils.",
|
||||
"Customizable Reading": "Lecture personnalisable",
|
||||
"Personalize every detail with adjustable fonts, layouts, themes, and advanced display settings for the perfect reading experience.": "Ajustez polices, thèmes et mise en page à votre goût.",
|
||||
"AI Read Aloud": "Lecture vocale IA",
|
||||
"Enjoy hands-free reading with natural-sounding AI voices that bring your books to life.": "Écoutez vos livres avec des voix naturelles IA.",
|
||||
"AI Translations": "Traductions IA",
|
||||
"Translate any text instantly with the power of Google, Azure, or DeepL—understand content in any language.": "Traduisez instantanément avec Google, Azure ou DeepL.",
|
||||
"Connect with fellow readers and get help fast in our friendly community channels.": "Rejoignez la communauté et recevez de l’aide rapidement.",
|
||||
"Unlimited AI Read Aloud Hours": "Lecture IA illimitée",
|
||||
"Listen without limits—convert as much text as you like into immersive audio.": "Convertissez le texte en audio sans limite.",
|
||||
"Unlock enhanced translation capabilities with more daily usage and advanced options.": "Plus de traductions et d’options avancées.",
|
||||
"DeepL Pro Access": "Accès DeepL Pro",
|
||||
"Enjoy faster responses and dedicated assistance whenever you need help.": "Support prioritaire et réponses rapides.",
|
||||
"Daily translation quota reached. Upgrade your plan to continue using AI translations.": "Limite quotidienne atteinte. Passez à un plan supérieur pour continuer.",
|
||||
"Includes All Plus Plan Benefits": "Inclut Tous les Avantages du Plan Plus",
|
||||
"Early Feature Access": "Accès Anticipé aux Fonctionnalités",
|
||||
"Be the first to explore new features, updates, and innovations before anyone else.": "Explorez en premier les nouvelles fonctionnalités, mises à jour et innovations.",
|
||||
"Advanced AI Tools": "Outils IA Avancés",
|
||||
"Harness powerful AI tools for smarter reading, translation, and content discovery.": "Exploitez des outils IA puissants pour une lecture intelligente, traduction et découverte de contenu.",
|
||||
"Translate up to 100,000 characters daily with the most accurate translation engine available.": "Traduisez jusqu'à 100 000 caractères par jour avec le moteur de traduction le plus précis.",
|
||||
"Translate up to 500,000 characters daily with the most accurate translation engine available.": "Traduisez jusqu'à 500 000 caractères par jour avec le moteur de traduction le plus précis.",
|
||||
"Securely store and access your entire reading collection with up to 5 GB of cloud storage.": "Stockez et accédez en sécurité à votre collection de lecture avec jusqu'à 5 GB de stockage cloud.",
|
||||
"Securely store and access your entire reading collection with up to 20 GB of cloud storage.": "Stockez et accédez en sécurité à votre collection de lecture avec jusqu'à 20 GB de stockage cloud.",
|
||||
"Deleted cloud backup of the book: {{title}}": "Suppression de la sauvegarde cloud du livre : {{title}}",
|
||||
"Are you sure to delete the cloud backup of the selected book?": "Êtes-vous sûr de vouloir supprimer la sauvegarde cloud du livre sélectionné ?",
|
||||
"Don't have an account? Sign up": "Vous n'avez pas de compte ? Inscrivez-vous",
|
||||
"What's New in Readest": "Quoi de neuf dans Readest",
|
||||
"Enter book title": "Saisir le titre du livre",
|
||||
"Subtitle": "Sous-titre",
|
||||
"Enter book subtitle": "Saisir le sous-titre du livre",
|
||||
"Enter author name": "Saisir le nom de l'auteur",
|
||||
"Series": "Série",
|
||||
"Enter series name": "Saisir le nom de la série",
|
||||
"Series Index": "Numéro de série",
|
||||
"Enter series index": "Saisir le numéro de série",
|
||||
"Total in Series": "Total dans la série",
|
||||
"Enter total books in series": "Saisir le nombre total de livres dans la série",
|
||||
"Enter publisher": "Saisir l'éditeur",
|
||||
"Publication Date": "Date de publication",
|
||||
"Identifier": "Identifiant",
|
||||
"Enter book description": "Saisir la description du livre",
|
||||
"Change cover image": "Changer l'image de couverture",
|
||||
"Replace": "Remplacer",
|
||||
"Unlock cover": "Déverrouiller la couverture",
|
||||
"Lock cover": "Verrouiller la couverture",
|
||||
"Auto-Retrieve Metadata": "Récupérer les métadonnées automatiquement",
|
||||
"Auto-Retrieve": "Récupération automatique",
|
||||
"Unlock all fields": "Déverrouiller tous les champs",
|
||||
"Unlock All": "Tout déverrouiller",
|
||||
"Lock all fields": "Verrouiller tous les champs",
|
||||
"Lock All": "Tout verrouiller",
|
||||
"Reset": "Réinitialiser",
|
||||
"Edit Metadata": "Modifier les métadonnées",
|
||||
"Locked": "Verrouillé",
|
||||
"Select Metadata Source": "Sélectionner la source des métadonnées",
|
||||
"Keep manual input": "Conserver la saisie manuelle",
|
||||
"Google Books": "Google Books",
|
||||
"Open Library": "Open Library",
|
||||
"Fiction, Science, History": "Fiction, Science, Histoire",
|
||||
"Select Cover Image": "Sélectionner l'image de couverture",
|
||||
"Open Book in New Window": "Ouvrir dans une nouvelle fenêtre",
|
||||
"Voices for {{lang}}": "Voix pour {{lang}}",
|
||||
"Yandex Translate": "Yandex Traduction",
|
||||
"YYYY or YYYY-MM-DD": "YYYY ou YYYY-MM-DD",
|
||||
"Restore Purchase": "Restaurer l'achat",
|
||||
"No purchases found to restore.": "Aucun achat trouvé à restaurer.",
|
||||
"Failed to restore purchases.": "Échec de la restauration des achats.",
|
||||
"Failed to manage subscription.": "Échec de la gestion de l'abonnement.",
|
||||
"Failed to load subscription plans.": "Échec du chargement des plans d'abonnement.",
|
||||
"year": "année",
|
||||
"Failed to create checkout session": "Échec de la création de la session de paiement",
|
||||
"Storage": "Stockage",
|
||||
"Terms of Service": "Conditions d'utilisation",
|
||||
"Privacy Policy": "Politique de confidentialité",
|
||||
"Disable Double Click": "Désactiver le double-clic",
|
||||
"TTS not supported for this document": "TTS non pris en charge pour ce document",
|
||||
"Reset Password": "Nouveau mot de passe",
|
||||
"Show Reading Progress": "Afficher la progression",
|
||||
"Reading Progress Style": "Style de progression",
|
||||
"Page Number": "Numéro de page",
|
||||
"Percentage": "Pourcentage",
|
||||
"Deleted local copy of the book: {{title}}": "Suppression de la copie locale du livre : {{title}}",
|
||||
"Failed to delete cloud backup of the book: {{title}}": "Échec de la suppression de la sauvegarde cloud du livre : {{title}}",
|
||||
"Failed to delete local copy of the book: {{title}}": "Échec de la suppression de la copie locale du livre : {{title}}",
|
||||
"Are you sure to delete the local copy of the selected book?": "Êtes-vous sûr de vouloir supprimer la copie locale du livre sélectionné ?",
|
||||
"Remove from Cloud & Device": "Supprimer du Cloud & de l'appareil",
|
||||
"Remove from Cloud Only": "Supprimer uniquement du Cloud",
|
||||
"Remove from Device Only": "Supprimer uniquement de l'appareil",
|
||||
"Disconnected": "Déconnecté",
|
||||
"KOReader Sync Settings": "Paramètres de synchronisation KOReader",
|
||||
"Sync Strategy": "Stratégie de synchronisation",
|
||||
"Ask on conflict": "Demander en cas de conflit",
|
||||
"Always use latest": "Toujours utiliser la dernière version",
|
||||
"Send changes only": "Envoyer uniquement les modifications",
|
||||
"Receive changes only": "Recevoir uniquement les modifications",
|
||||
"Disabled": "Désactivé",
|
||||
"Checksum Method": "Méthode de somme de contrôle",
|
||||
"File Content (recommended)": "Contenu du fichier (recommandé)",
|
||||
"File Name": "Nom du fichier",
|
||||
"Device Name": "Nom de l'appareil",
|
||||
"Sync Tolerance": "Tolérance de synchronisation",
|
||||
"Connect to your KOReader Sync server.": "Connectez-vous à votre serveur de synchronisation KOReader.",
|
||||
"Server URL": "URL du serveur",
|
||||
"Username": "Nom d'utilisateur",
|
||||
"Your Username": "Votre nom d'utilisateur",
|
||||
"Password": "Mot de passe",
|
||||
"Connect": "Connecter",
|
||||
"KOReader Sync": "Synchronisation KOReader",
|
||||
"Sync Conflict": "Conflit de synchronisation",
|
||||
"Sync reading progress from \"{{deviceName}}\"?": "Synchroniser la progression de lecture depuis \"{{deviceName}}\"?",
|
||||
"another device": "un autre appareil",
|
||||
"Local Progress": "Progression locale",
|
||||
"Remote Progress": "Progression distante",
|
||||
"Page {{page}} of {{total}} ({{percentage}}%)": "Page {{page}} sur {{total}} ({{percentage}}%)",
|
||||
"Current position": "Position actuelle",
|
||||
"Approximately page {{page}} of {{total}} ({{percentage}}%)": "Environ page {{page}} sur {{total}} ({{percentage}}%)",
|
||||
"Approximately {{percentage}}%": "Environ {{percentage}}%",
|
||||
"Failed to connect": "Échec de la connexion",
|
||||
"Sync Server Connected": "Serveur de synchronisation connecté",
|
||||
"Precision: {{precision}} digits after the decimal": "Précision : {{precision}} chiffres après la virgule",
|
||||
"Sync as {{userDisplayName}}": "Synchroniser en tant que {{userDisplayName}}"
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
"Confirm Deletion": "हटाने की पुष्टि करें",
|
||||
"Copied to notebook": "नोटबुक में कॉपी किया गया",
|
||||
"Copy": "कॉपी",
|
||||
"Custom CSS": "कस्टम CSS",
|
||||
"Dark Mode": "डार्क मोड",
|
||||
"Default": "डिफ़ॉल्ट",
|
||||
"Default Font": "डिफ़ॉल्ट फ़ॉन्ट",
|
||||
@@ -28,7 +27,6 @@
|
||||
"Dictionary": "शब्दकोश",
|
||||
"Download Readest": "Readest डाउनलोड करें",
|
||||
"Edit": "संपादित करें",
|
||||
"Enter your custom CSS here...": "अपना कस्टम CSS यहां दर्ज करें...",
|
||||
"Excerpts": "अंश",
|
||||
"Failed to import book(s): {{filenames}}": "पुस्तक(ें) आयात करने में विफल: {{filenames}}",
|
||||
"Fast": "तेज़",
|
||||
@@ -48,7 +46,6 @@
|
||||
"Horizontal Direction": "क्षैतिज दिशा",
|
||||
"Hyphenation": "हाइफ़नेशन",
|
||||
"Import Books": "पुस्तकें आयात करें",
|
||||
"Language:": "भाषा:",
|
||||
"Layout": "लेआउट",
|
||||
"Light Mode": "लाइट मोड",
|
||||
"Loading...": "लोड हो रहा है...",
|
||||
@@ -70,8 +67,8 @@
|
||||
"Paging Animation": "पेजिंग एनीमेशन",
|
||||
"Paragraph": "पैराग्राफ",
|
||||
"Parallel Read": "समानांतर पढ़ें",
|
||||
"Published:": "प्रकाशित:",
|
||||
"Publisher:": "प्रकाशक:",
|
||||
"Published": "प्रकाशित",
|
||||
"Publisher": "प्रकाशक",
|
||||
"Reading Progress Synced": "पढ़ने की प्रगति सिंक की गई",
|
||||
"Reload Page": "पृष्ठ रीलोड करें",
|
||||
"Reveal in File Explorer": "फ़ाइल एक्सप्लोरर में दिखाएं",
|
||||
@@ -96,16 +93,15 @@
|
||||
"Slow": "धीमा",
|
||||
"Solarized": "सोलराइज्ड",
|
||||
"Speak": "बोलें",
|
||||
"Subjects:": "विषय:",
|
||||
"Subjects": "विषय",
|
||||
"System Fonts": "सिस्टम फ़ॉन्ट्स",
|
||||
"TTS not supported in this device": "यह डिवाइस TTS का समर्थन नहीं करता",
|
||||
"Theme Color": "थीम रंग",
|
||||
"Theme Mode": "थीम मोड",
|
||||
"Translate": "अनुवाद करें",
|
||||
"Translated Text": "अनुवादित पाठ",
|
||||
"Unknown": "अज्ञात",
|
||||
"Untitled": "शीर्षकहीन",
|
||||
"Updated:": "अपडेट किया गया:",
|
||||
"Updated": "अपडेट किया गया",
|
||||
"Version {{version}}": "संस्करण {{version}}",
|
||||
"Vertical Direction": "ऊर्ध्वाधर दिशा",
|
||||
"Welcome to your library. You can import your books here and read them anytime.": "आपकी लाइब्रेरी में आपका स्वागत है। आप यहां अपनी पुस्तकें आयात कर सकते हैं और कभी भी पढ़ सकते हैं।",
|
||||
@@ -114,8 +110,6 @@
|
||||
"Your Library": "आपकी लाइब्रेरी",
|
||||
"TTS not supported for PDF": "PDF के लिए TTS समर्थित नहीं है",
|
||||
"Override Book Font": "पुस्तक फ़ॉन्ट ओवरराइड करें",
|
||||
"Vertical Margins (px)": "ऊर्ध्वाधर मार्जिन (px)",
|
||||
"Horizontal Margins (%)": "क्षैतिज मार्जिन (%)",
|
||||
"Apply to All Books": "सभी पुस्तकों पर लागू करें",
|
||||
"Apply to This Book": "इस पुस्तक पर लागू करें",
|
||||
"Unable to fetch the translation. Try again later.": "अनुवाद प्राप्त करने में असमर्थ। बाद में पुनः प्रयास करें।",
|
||||
@@ -130,8 +124,6 @@
|
||||
"Failed to download book: {{title}}": "पुस्तक डाउनलोड करने में विफल: {{title}}",
|
||||
"Upload Book": "पुस्तक अपलोड करें",
|
||||
"Auto Upload Books to Cloud": "पुस्तकें बादल में स्वचालित रूप से अपलोड करें",
|
||||
"{{percentage}}% of Cloud Storage Used.": "{{percentage}}% क्लाउड स्टोरेज का उपयोग किया गया है।",
|
||||
"Storage": "स्टोरेज",
|
||||
"Book deleted: {{title}}": "पुस्तक हटाई गई: {{title}}",
|
||||
"Failed to delete book: {{title}}": "पुस्तक हटाने में विफल: {{title}}",
|
||||
"Check Updates on Start": "शुरू में अपडेट जांचें",
|
||||
@@ -163,7 +155,7 @@
|
||||
"Create a Password": "पासवर्ड बनाएं",
|
||||
"Sign up": "साइन अप करें",
|
||||
"Signing up...": "साइन अप हो रहा है...",
|
||||
"Don’t have an account? Sign up": "कोई खाता नहीं है? साइन अप करें",
|
||||
"Don't have an account? Sign up": "कोई खाता नहीं है? साइन अप करें",
|
||||
"Check your email for the confirmation link": "पुष्टि लिंक के लिए अपना ईमेल जांचें",
|
||||
"Signing in ...": "साइन इन हो रहा है ...",
|
||||
"Send a magic link email": "जादू लिंक ईमेल भेजें",
|
||||
@@ -187,35 +179,13 @@
|
||||
"Sign in with GitHub": "GitHub के साथ साइन इन करें",
|
||||
"Account": "खाता",
|
||||
"Failed to delete user. Please try again later.": "उपयोगकर्ता को हटाने में विफल। कृपया बाद में पुनः प्रयास करें।",
|
||||
"Unlimited Offline/Online Reading": "असीमित ऑफलाइन/ऑनलाइन पढ़ना",
|
||||
"Unlimited Cloud Sync Devices": "असीमित क्लाउड सिंक उपकरण",
|
||||
"Essential Text-to-Speech Voices": "आवश्यक टेक्स्ट-टू-स्पीच आवाज़ें",
|
||||
"DeepL Free Access": "DeepL मुफ्त पहुंच",
|
||||
"Community Support": "सामुदायिक सहायता",
|
||||
"500 MB Cloud Sync Space": "500 MB क्लाउड सिंक स्पेस",
|
||||
"Includes All Free Tier Benefits": "सभी फ्री टियर लाभ शामिल हैं",
|
||||
"AI Summaries": "AI सारांश",
|
||||
"AI Translations": "AI अनुवाद",
|
||||
"Priority Support": "प्राथमिकता सहायता",
|
||||
"DeepL Pro Access": "DeepL प्रो पहुंच",
|
||||
"Expanded Text-to-Speech Voices": "विस्तारित टेक्स्ट-टू-स्पीच आवाज़ें",
|
||||
"2000 MB Cloud Sync Space": "2000 MB क्लाउड सिंक स्पेस",
|
||||
"Includes All Plus Tier Benefits": "सभी प्लस टियर लाभ शामिल हैं",
|
||||
"Unlimited AI Summaries": "असीमित AI सारांश",
|
||||
"Unlimited AI Translations": "असीमित AI अनुवाद",
|
||||
"Unlimited DeepL Pro Access": "असीमित DeepL प्रो पहुंच",
|
||||
"Advanced AI Tools": "उन्नत AI टूल्स",
|
||||
"Early Feature Access": "प्रारंभिक फीचर पहुंच",
|
||||
"10 GB Cloud Sync Space": "10 GB क्लाउड सिंक स्पेस",
|
||||
"Loading profile...": "प्रोफ़ाइल लोड हो रहा है...",
|
||||
"Features": "विशेषताएं",
|
||||
"Delete Account": "खाता हटाएं",
|
||||
"Delete Your Account?": "अपना खाता हटाएं?",
|
||||
"This action cannot be undone. All your data in the cloud will be permanently deleted.": "यह क्रिया पूर्ववत नहीं की जा सकती। क्लाउड में आपका सभी डेटा स्थायी रूप से हटा दिया जाएगा।",
|
||||
"Delete Permanently": "स्थायी रूप से हटाएं",
|
||||
"Free Tier": "फ्री टियर",
|
||||
"Plus Tier": "प्लस टियर",
|
||||
"Pro Tier": "प्रो टियर",
|
||||
"RTL Direction": "RTL दिशा",
|
||||
"Maximum Column Height": "कॉलम की अधिकतम ऊँचाई",
|
||||
"Maximum Column Width": "कॉलम की अधिकतम चौड़ाई",
|
||||
@@ -266,13 +236,13 @@
|
||||
"Clear Search": "खोज साफ करें",
|
||||
"Header & Footer": "हेडर और फ़ुटर",
|
||||
"Apply also in Scrolled Mode": "स्क्रॉल मोड में भी लागू करें",
|
||||
"A new version of Readest is Available!": "Readest का एक नया संस्करण उपलब्ध है!",
|
||||
"A new version of Readest is available!": "Readest का एक नया संस्करण उपलब्ध है!",
|
||||
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} उपलब्ध है (स्थापित संस्करण {{currentVersion}})।",
|
||||
"Download and install now?": "अब डाउनलोड और इंस्टॉल करें?",
|
||||
"Downloading {{downloaded}} of {{contentLength}}": "डाउनलोड कर रहा है {{downloaded}} का {{contentLength}}",
|
||||
"Download finished": "डाउनलोड पूरा हुआ",
|
||||
"DOWNLOAD & INSTALL": "डाउनलोड और इंस्टॉल करें",
|
||||
"Changelog:": "चेंजलॉग:",
|
||||
"Changelog": "चेंजलॉग",
|
||||
"Software Update": "सॉफ़्टवेयर अपडेट",
|
||||
"Title": "शीर्षक",
|
||||
"Date Read": "पढ़ने की तारीख",
|
||||
@@ -281,10 +251,9 @@
|
||||
"Ascending": "आरोही",
|
||||
"Descending": "अवरोही",
|
||||
"Sort by...": "द्वारा क्रमबद्ध करें...",
|
||||
"Added:": "जोड़ा गया:",
|
||||
"Format:": "फॉर्मेट:",
|
||||
"Added": "जोड़ा गया",
|
||||
"GuanKiapTsingKhai-T": "GuanKiapTsingKhai-T",
|
||||
"Description:": "विवरण:",
|
||||
"Description": "विवरण",
|
||||
"No description available": "कोई विवरण उपलब्ध नहीं है",
|
||||
"List": "सूची",
|
||||
"Grid": "ग्रिड",
|
||||
@@ -301,7 +270,7 @@
|
||||
"Checking for updates...": "अपडेट की जांच कर रहा है...",
|
||||
"Error checking for updates": "अपडेट की जांच करते समय त्रुटि",
|
||||
"Details": "विवरण",
|
||||
"File Size:": "फ़ाइल आकार:",
|
||||
"File Size": "फ़ाइल आकार",
|
||||
"Auto Detect": "स्वचालित पहचान",
|
||||
"Next Section": "अगला अनुभाग",
|
||||
"Previous Section": "पिछला अनुभाग",
|
||||
@@ -342,14 +311,12 @@
|
||||
"Synced at {{time}}": "{{time}} पर सिंक किया गया",
|
||||
"Never synced": "कभी सिंक नहीं किया गया",
|
||||
"Show Remaining Time": "शेष समय दिखाएं",
|
||||
"Show Page Number": "पृष्ठ संख्या दिखाएं",
|
||||
"{{time}} min left in chapter": "{{time}} मिनट शेष अध्याय में",
|
||||
"Override Book Color": "पुस्तक रंग ओवरराइड करें",
|
||||
"Login Required": "लॉगिन ज़रूरी",
|
||||
"Quota Exceeded": "सीमा पार",
|
||||
"{{percentage}}% of Daily Translation Characters Used.": "{{percentage}}% दैनिक अनुवाद वर्णों का उपयोग किया गया है।",
|
||||
"Translation Characters": "अनुवाद वर्ण",
|
||||
"Daily translation quota reached. Select another translate service to proceed.": "दैनिक अनुवाद कोटा पूरा हो गया है। आगे बढ़ने के लिए कोई अन्य अनुवाद सेवा चुनें।",
|
||||
"{{engine}}: {{count}} voices_one": "{{engine}}: {{count}} आवाज़",
|
||||
"{{engine}}: {{count}} voices_other": "{{engine}}: {{count}} आवाज़ें",
|
||||
"Something went wrong. Don't worry, our team has been notified and we're working on a fix.": "कुछ गलत हो गया। चिंता न करें, हमारी टीम को सूचित कर दिया गया है और हम एक समाधान पर काम कर रहे हैं।",
|
||||
@@ -359,5 +326,183 @@
|
||||
"Contact Support": "सहायता से संपर्क करें",
|
||||
"Code Highlighting": "कोड हाइलाइटिंग",
|
||||
"Enable Highlighting": "हाइलाइटिंग सक्षम करें",
|
||||
"Code Language": "कोड भाषा"
|
||||
"Code Language": "कोड भाषा",
|
||||
"Top Margin (px)": "ऊपरी हाशिया (px)",
|
||||
"Bottom Margin (px)": "निचला हाशिया (px)",
|
||||
"Right Margin (px)": "दायां हाशिया (px)",
|
||||
"Left Margin (px)": "बायां हाशिया (px)",
|
||||
"Column Gap (%)": "स्तंभों के बीच की दूरी (%)",
|
||||
"Always Show Status Bar": "स्थिति पट्टी हमेशा दिखाएं",
|
||||
"Translation Not Available": "अनुवाद उपलब्ध नहीं है",
|
||||
"Custom Content CSS": "सामग्री का CSS",
|
||||
"Enter CSS for book content styling...": "पुस्तक सामग्री के लिए CSS दर्ज करें...",
|
||||
"Custom Reader UI CSS": "इंटरफ़ेस का CSS",
|
||||
"Enter CSS for reader interface styling...": "रीडर इंटरफ़ेस के लिए CSS दर्ज करें...",
|
||||
"Crop": "काटें",
|
||||
"Book Covers": "पुस्तक कवर",
|
||||
"Fit": "फिट",
|
||||
"Reset {{settings}}": "रीसेट {{settings}}",
|
||||
"Reset Settings": "सेटिंग्स रीसेट करें",
|
||||
"{{count}} pages left in chapter_one": "इस अध्याय में {{count}} पृष्ठ शेष हैं",
|
||||
"{{count}} pages left in chapter_other": "इस अध्याय में {{count}} पृष्ठ शेष हैं",
|
||||
"Show Remaining Pages": "शेष पृष्ठ दिखाएँ",
|
||||
"Source Han Serif CN VF": "Source Han Serif",
|
||||
"Huiwen-mincho": "Huiwen Mincho",
|
||||
"KingHwa_OldSong": "KingHwa Song",
|
||||
"Manage Subscription": "सदस्यता प्रबंधित करें",
|
||||
"Coming Soon": "जल्द आ रहा है",
|
||||
"Upgrade to {{plan}}": "{{plan}} में अपग्रेड करें",
|
||||
"Upgrade to Plus or Pro": "Plus या Pro में अपग्रेड करें",
|
||||
"Current Plan": "वर्तमान योजना",
|
||||
"Plan Limits": "योजना की सीमाएँ",
|
||||
"Processing your payment...": "आपका भुगतान संसाधित हो रहा है...",
|
||||
"Please wait while we confirm your subscription.": "कृपया प्रतीक्षा करें, हम आपकी सदस्यता की पुष्टि कर रहे हैं।",
|
||||
"Payment Processing": "भुगतान संसाधित हो रहा है",
|
||||
"Your payment is being processed. This usually takes a few moments.": "आपका भुगतान संसाधित हो रहा है। इसमें कुछ क्षण लग सकते हैं।",
|
||||
"Payment Failed": "भुगतान विफल",
|
||||
"We couldn't process your subscription. Please try again or contact support if the issue persists.": "हम आपकी सदस्यता संसाधित नहीं कर सके। कृपया पुनः प्रयास करें या समस्या बने रहने पर सहायता से संपर्क करें।",
|
||||
"Back to Profile": "प्रोफ़ाइल पर वापस जाएँ",
|
||||
"Subscription Successful!": "सदस्यता सफल हुई!",
|
||||
"Thank you for your subscription! Your payment has been processed successfully.": "आपकी सदस्यता के लिए धन्यवाद! आपका भुगतान सफलतापूर्वक संसाधित हो गया है।",
|
||||
"Email:": "ईमेल:",
|
||||
"Plan:": "योजना:",
|
||||
"Amount:": "राशि:",
|
||||
"Subscription ID:": "सदस्यता आईडी:",
|
||||
"Go to Library": "लाइब्रेरी पर जाएँ",
|
||||
"Need help? Contact our support team at support@readest.com": "मदद चाहिए? हमारी सहायता टीम से संपर्क करें: support@readest.com",
|
||||
"Free Plan": "मुफ्त योजना",
|
||||
"month": "महीना",
|
||||
"AI Translations (per day)": "AI अनुवाद (प्रतिदिन)",
|
||||
"Plus Plan": "Plus योजना",
|
||||
"Includes All Free Plan Benefits": "सभी मुफ्त योजना के लाभ शामिल हैं",
|
||||
"Pro Plan": "Pro योजना",
|
||||
"More AI Translations": "और अधिक AI अनुवाद",
|
||||
"Complete Your Subscription": "अपनी सदस्यता पूरी करें",
|
||||
"{{percentage}}% of Cloud Sync Space Used.": "{{percentage}}% क्लाउड सिंक स्पेस का उपयोग किया गया है।",
|
||||
"Cloud Sync Storage": "क्लाउड सिंक स्टोरेज",
|
||||
"Disable": "अक्षम करें",
|
||||
"Enable": "सक्षम करें",
|
||||
"Upgrade to Readest Premium": "Readest प्रीमियम में अपग्रेड करें",
|
||||
"Show Source Text": "मूल पाठ दिखाएं",
|
||||
"Cross-Platform Sync": "क्रॉस-प्लेटफ़ॉर्म सिंक",
|
||||
"Seamlessly sync your library, progress, highlights, and notes across all your devices—never lose your place again.": "सभी डिवाइस पर लाइब्रेरी, प्रगति और नोट सिंक करें।",
|
||||
"Customizable Reading": "अनुकूलन योग्य पढ़ाई",
|
||||
"Personalize every detail with adjustable fonts, layouts, themes, and advanced display settings for the perfect reading experience.": "फॉन्ट, लेआउट और थीम अपनी पसंद से बदलें।",
|
||||
"AI Read Aloud": "एआई वाचन",
|
||||
"Enjoy hands-free reading with natural-sounding AI voices that bring your books to life.": "प्राकृतिक एआई आवाज़ों में किताबें सुनें।",
|
||||
"AI Translations": "एआई अनुवाद",
|
||||
"Translate any text instantly with the power of Google, Azure, or DeepL—understand content in any language.": "Google, Azure या DeepL से तुरंत अनुवाद करें।",
|
||||
"Connect with fellow readers and get help fast in our friendly community channels.": "समुदाय से जुड़ें और सहायता पाएं।",
|
||||
"Unlimited AI Read Aloud Hours": "असीमित एआई वाचन",
|
||||
"Listen without limits—convert as much text as you like into immersive audio.": "जितना चाहें उतना ऑडियो में सुनें।",
|
||||
"Unlock enhanced translation capabilities with more daily usage and advanced options.": "अधिक अनुवाद और उन्नत विकल्प पाएं।",
|
||||
"DeepL Pro Access": "DeepL प्रो एक्सेस",
|
||||
"Enjoy faster responses and dedicated assistance whenever you need help.": "प्राथमिक सहायता पाएं।",
|
||||
"Daily translation quota reached. Upgrade your plan to continue using AI translations.": "दैनिक सीमा पूरी हुई। योजना बढ़ाएं।",
|
||||
"Includes All Plus Plan Benefits": "सभी प्लस प्लान लाभ शामिल",
|
||||
"Early Feature Access": "नई सुविधाओं की पहले पहुंच",
|
||||
"Be the first to explore new features, updates, and innovations before anyone else.": "नई सुविधाएं, अपडेट और नवाचार सबसे पहले देखें।",
|
||||
"Advanced AI Tools": "उन्नत AI उपकरण",
|
||||
"Harness powerful AI tools for smarter reading, translation, and content discovery.": "स्मार्ट पढ़ने, अनुवाद और कंटेंट खोजने के लिए शक्तिशाली AI उपकरण का उपयोग करें।",
|
||||
"Translate up to 100,000 characters daily with the most accurate translation engine available.": "सबसे सटीक अनुवाद इंजन के साथ दैनिक 1 लाख तक अक्षर अनुवाद करें।",
|
||||
"Translate up to 500,000 characters daily with the most accurate translation engine available.": "सबसे सटीक अनुवाद इंजन के साथ दैनिक 5 लाख तक अक्षर अनुवाद करें।",
|
||||
"Securely store and access your entire reading collection with up to 5 GB of cloud storage.": "5 GB सुरक्षित क्लाउड स्टोरेज के साथ अपना संपूर्ण पठन संग्रह संग्रहीत करें।",
|
||||
"Securely store and access your entire reading collection with up to 20 GB of cloud storage.": "20 GB सुरक्षित क्लाउड स्टोरेज के साथ अपना संपूर्ण पठन संग्रह संग्रहीत करें।",
|
||||
"Deleted cloud backup of the book: {{title}}": "पुस्तक का क्लाउड बैकअप हटाया गया: {{title}}",
|
||||
"Are you sure to delete the cloud backup of the selected book?": "क्या आप सुनिश्चित हैं कि आप चयनित पुस्तक का क्लाउड बैकअप हटाना चाहते हैं?",
|
||||
"What's New in Readest": "Readest में नया क्या है",
|
||||
"Enter book title": "पुस्तक का शीर्षक दर्ज करें",
|
||||
"Subtitle": "उपशीर्षक",
|
||||
"Enter book subtitle": "पुस्तक का उपशीर्षक दर्ज करें",
|
||||
"Enter author name": "लेखक का नाम दर्ज करें",
|
||||
"Series": "श्रृंखला",
|
||||
"Enter series name": "श्रृंखला का नाम दर्ज करें",
|
||||
"Series Index": "श्रृंखला संख्या",
|
||||
"Enter series index": "श्रृंखला संख्या दर्ज करें",
|
||||
"Total in Series": "श्रृंखला में कुल",
|
||||
"Enter total books in series": "श्रृंखला में कुल पुस्तकों की संख्या दर्ज करें",
|
||||
"Enter publisher": "प्रकाशक का नाम दर्ज करें",
|
||||
"Publication Date": "प्रकाशन दिनांक",
|
||||
"Identifier": "पहचानकर्ता",
|
||||
"Enter book description": "पुस्तक का विवरण दर्ज करें",
|
||||
"Change cover image": "कवर इमेज बदलें",
|
||||
"Replace": "बदलें",
|
||||
"Unlock cover": "कवर अनलॉक करें",
|
||||
"Lock cover": "कवर लॉक करें",
|
||||
"Auto-Retrieve Metadata": "मेटाडेटा स्वचालित रूप से प्राप्त करें",
|
||||
"Auto-Retrieve": "स्वचालित प्राप्ति",
|
||||
"Unlock all fields": "सभी फील्ड अनलॉक करें",
|
||||
"Unlock All": "सभी अनलॉक करें",
|
||||
"Lock all fields": "सभी फील्ड लॉक करें",
|
||||
"Lock All": "सभी लॉक करें",
|
||||
"Reset": "रीसेट",
|
||||
"Edit Metadata": "मेटाडेटा संपादित करें",
|
||||
"Locked": "लॉक है",
|
||||
"Select Metadata Source": "मेटाडेटा स्रोत चुनें",
|
||||
"Keep manual input": "मैन्युअल इनपुट रखें",
|
||||
"Google Books": "गूगल बुक्स",
|
||||
"Open Library": "ओपन लाइब्रेरी",
|
||||
"Fiction, Science, History": "काल्पनिक, विज्ञान, इतिहास",
|
||||
"Select Cover Image": "कवर इमेज चुनें",
|
||||
"Open Book in New Window": "नई विंडो में पुस्तक खोलें",
|
||||
"Voices for {{lang}}": "{{lang}} के लिए आवाज़ें",
|
||||
"Yandex Translate": "यांडेक्स अनुवाद",
|
||||
"YYYY or YYYY-MM-DD": "YYYY या YYYY-MM-DD",
|
||||
"Restore Purchase": "खरीदारी पुनर्स्थापित करें",
|
||||
"No purchases found to restore.": "पुनर्स्थापित करने के लिए कोई खरीदारी नहीं मिली।",
|
||||
"Failed to restore purchases.": "खरीदारी पुनर्स्थापित करने में विफल।",
|
||||
"Failed to manage subscription.": "सदस्यता प्रबंधित करने में विफल।",
|
||||
"Failed to load subscription plans.": "सदस्यता योजनाएँ लोड करने में विफल।",
|
||||
"year": "वर्ष",
|
||||
"Failed to create checkout session": "चेकआउट सत्र बनाने में विफल",
|
||||
"Storage": "भंडारण",
|
||||
"Terms of Service": "सेवा की शर्तें",
|
||||
"Privacy Policy": "गोपनीयता नीति",
|
||||
"Disable Double Click": "डबल क्लिक अक्षम करें",
|
||||
"TTS not supported for this document": "TTS इस दस्तावेज़ के लिए समर्थित नहीं है",
|
||||
"Reset Password": "पासवर्ड रीसेट करें",
|
||||
"Show Reading Progress": "पढ़ने की प्रगति दिखाएं",
|
||||
"Reading Progress Style": "पढ़ने की प्रगति शैली",
|
||||
"Page Number": "पृष्ठ संख्या",
|
||||
"Percentage": "प्रतिशत",
|
||||
"Deleted local copy of the book: {{title}}": "पुस्तक की स्थानीय प्रति हटाई गई: {{title}}",
|
||||
"Failed to delete cloud backup of the book: {{title}}": "पुस्तक का क्लाउड बैकअप हटाने में विफल: {{title}}",
|
||||
"Failed to delete local copy of the book: {{title}}": "पुस्तक की स्थानीय प्रति हटाने में विफल: {{title}}",
|
||||
"Are you sure to delete the local copy of the selected book?": "क्या आप सुनिश्चित हैं कि आप चयनित पुस्तक की स्थानीय प्रति हटाना चाहते हैं?",
|
||||
"Remove from Cloud & Device": "क्लाउड और डिवाइस से हटाएँ",
|
||||
"Remove from Cloud Only": "केवल क्लाउड से हटाएँ",
|
||||
"Remove from Device Only": "केवल डिवाइस से हटाएँ",
|
||||
"Disconnected": "अविचलित",
|
||||
"KOReader Sync Settings": "KOReader सिंक सेटिंग्स",
|
||||
"Sync Strategy": "सिंक रणनीति",
|
||||
"Ask on conflict": "संघर्ष पर पूछें",
|
||||
"Always use latest": "हमेशा नवीनतम का उपयोग करें",
|
||||
"Send changes only": "केवल परिवर्तन भेजें",
|
||||
"Receive changes only": "केवल परिवर्तन प्राप्त करें",
|
||||
"Disabled": "अक्षम",
|
||||
"Checksum Method": "चेकसम विधि",
|
||||
"File Content (recommended)": "फाइल सामग्री (अनुशंसित)",
|
||||
"File Name": "फाइल नाम",
|
||||
"Device Name": "डिवाइस नाम",
|
||||
"Sync Tolerance": "सिंक सहिष्णुता",
|
||||
"Connect to your KOReader Sync server.": "अपने KOReader सिंक सर्वर से कनेक्ट करें।",
|
||||
"Server URL": "सर्वर यूआरएल",
|
||||
"Username": "उपयोगकर्ता नाम",
|
||||
"Your Username": "आपका उपयोगकर्ता नाम",
|
||||
"Password": "पासवर्ड",
|
||||
"Connect": "कनेक्ट करें",
|
||||
"KOReader Sync": "KOReader सिंक",
|
||||
"Sync Conflict": "सिंक संघर्ष",
|
||||
"Sync reading progress from \"{{deviceName}}\"?": "क्या आप \"{{deviceName}}\" से पढ़ने की प्रगति को सिंक करना चाहते हैं?",
|
||||
"another device": "दूसरा डिवाइस",
|
||||
"Local Progress": "स्थानीय प्रगति",
|
||||
"Remote Progress": "दूरस्थ प्रगति",
|
||||
"Page {{page}} of {{total}} ({{percentage}}%)": "पृष्ठ {{page}} कुल {{total}} ({{percentage}}%)",
|
||||
"Current position": "वर्तमान स्थिति",
|
||||
"Approximately page {{page}} of {{total}} ({{percentage}}%)": "लगभग पृष्ठ {{page}} कुल {{total}} ({{percentage}}%)",
|
||||
"Approximately {{percentage}}%": "लगभग {{percentage}}%",
|
||||
"Failed to connect": "कनेक्ट करने में विफल",
|
||||
"Sync Server Connected": "सिंक सर्वर कनेक्टेड",
|
||||
"Precision: {{precision}} digits after the decimal": "सटीकता: {{precision}} दशमलव के बाद अंक",
|
||||
"Sync as {{userDisplayName}}": "सिंक के रूप में {{userDisplayName}}"
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
"Confirm Deletion": "Konfirmasi Penghapusan",
|
||||
"Copied to notebook": "Disalin ke buku catatan",
|
||||
"Copy": "Salin",
|
||||
"Custom CSS": "CSS Kustom",
|
||||
"Dark Mode": "Mode Gelap",
|
||||
"Default": "Default",
|
||||
"Default Font": "Font Default",
|
||||
@@ -28,7 +27,6 @@
|
||||
"Dictionary": "Kamus",
|
||||
"Download Readest": "Unduh Readest",
|
||||
"Edit": "Edit",
|
||||
"Enter your custom CSS here...": "Masukkan CSS kustom Anda di sini...",
|
||||
"Excerpts": "Kutipan",
|
||||
"Failed to import book(s): {{filenames}}": "Gagal mengimpor buku: {{filenames}}",
|
||||
"Fast": "Cepat",
|
||||
@@ -48,7 +46,6 @@
|
||||
"Horizontal Direction": "Arah Horizontal",
|
||||
"Hyphenation": "Pemenggalan Kata",
|
||||
"Import Books": "Impor Buku",
|
||||
"Language:": "Bahasa:",
|
||||
"Layout": "Tata Letak",
|
||||
"Light Mode": "Mode Terang",
|
||||
"Loading...": "Memuat...",
|
||||
@@ -70,8 +67,8 @@
|
||||
"Paging Animation": "Animasi Halaman",
|
||||
"Paragraph": "Paragraf",
|
||||
"Parallel Read": "Baca Paralel",
|
||||
"Published:": "Diterbitkan:",
|
||||
"Publisher:": "Penerbit:",
|
||||
"Published": "Diterbitkan",
|
||||
"Publisher": "Penerbit",
|
||||
"Reading Progress Synced": "Progres membaca disinkronkan",
|
||||
"Reload Page": "Muat Ulang Halaman",
|
||||
"Reveal in File Explorer": "Tampilkan di File Explorer",
|
||||
@@ -96,16 +93,15 @@
|
||||
"Slow": "Lambat",
|
||||
"Solarized": "Solarized",
|
||||
"Speak": "Bicara",
|
||||
"Subjects:": "Subjek:",
|
||||
"Subjects": "Subjek",
|
||||
"System Fonts": "Font Sistem",
|
||||
"TTS not supported in this device": "TTS tidak didukung di perangkat ini",
|
||||
"Theme Color": "Warna Tema",
|
||||
"Theme Mode": "Mode Tema",
|
||||
"Translate": "Terjemahkan",
|
||||
"Translated Text": "Teks Terjemahan",
|
||||
"Unknown": "Tidak Diketahui",
|
||||
"Untitled": "Tanpa Judul",
|
||||
"Updated:": "Diperbarui:",
|
||||
"Updated": "Diperbarui",
|
||||
"Version {{version}}": "Versi {{version}}",
|
||||
"Vertical Direction": "Arah Vertikal",
|
||||
"Welcome to your library. You can import your books here and read them anytime.": "Selamat datang di perpustakaan Anda. Anda dapat mengimpor buku-buku Anda di sini dan membacanya kapan saja.",
|
||||
@@ -114,8 +110,6 @@
|
||||
"Your Library": "Perpustakaan Anda",
|
||||
"TTS not supported for PDF": "TTS tidak didukung untuk PDF",
|
||||
"Override Book Font": "Ganti Font Buku",
|
||||
"Vertical Margins (px)": "Margin Vertikal (px)",
|
||||
"Horizontal Margins (%)": "Margin Horizontal (%)",
|
||||
"Apply to All Books": "Terapkan ke semua buku",
|
||||
"Apply to This Book": "Terapkan ke buku ini",
|
||||
"Unable to fetch the translation. Try again later.": "Tidak dapat mengambil terjemahan. Coba lagi nanti.",
|
||||
@@ -130,8 +124,6 @@
|
||||
"Failed to download book: {{title}}": "Gagal mengunduh buku: {{title}}",
|
||||
"Upload Book": "Unggah Buku",
|
||||
"Auto Upload Books to Cloud": "Unggah Buku Otomatis ke Cloud",
|
||||
"{{percentage}}% of Cloud Storage Used.": "{{percentage}}% dari Penyimpanan Cloud Digunakan.",
|
||||
"Storage": "Penyimpanan",
|
||||
"Book deleted: {{title}}": "Buku dihapus: {{title}}",
|
||||
"Failed to delete book: {{title}}": "Gagal menghapus buku: {{title}}",
|
||||
"Check Updates on Start": "Periksa Pembaruan saat Memulai",
|
||||
@@ -163,7 +155,7 @@
|
||||
"Create a Password": "Buat kata sandi",
|
||||
"Sign up": "Daftar",
|
||||
"Signing up...": "Sedang mendaftar...",
|
||||
"Don’t have an account? Sign up": "Belum punya akun? Daftar",
|
||||
"Don't have an account? Sign up": "Belum punya akun? Daftar",
|
||||
"Check your email for the confirmation link": "Periksa email Anda untuk tautan konfirmasi",
|
||||
"Signing in ...": "Sedang masuk ...",
|
||||
"Send a magic link email": "Kirim email tautan ajaib",
|
||||
@@ -187,35 +179,13 @@
|
||||
"Sign in with GitHub": "Masuk dengan GitHub",
|
||||
"Account": "Akun",
|
||||
"Failed to delete user. Please try again later.": "Gagal menghapus pengguna. Silakan coba lagi nanti.",
|
||||
"Unlimited Offline/Online Reading": "Membaca Offline/Online Tanpa Batas",
|
||||
"Unlimited Cloud Sync Devices": "Perangkat Sinkronisasi Cloud Tanpa Batas",
|
||||
"Essential Text-to-Speech Voices": "Suara Text-to-Speech Esensial",
|
||||
"DeepL Free Access": "Akses DeepL Gratis",
|
||||
"Community Support": "Dukungan Komunitas",
|
||||
"500 MB Cloud Sync Space": "Ruang Sinkronisasi Cloud 500 MB",
|
||||
"Includes All Free Tier Benefits": "Termasuk Semua Manfaat Tingkat Gratis",
|
||||
"AI Summaries": "Ringkasan AI",
|
||||
"AI Translations": "Terjemahan AI",
|
||||
"Priority Support": "Dukungan Prioritas",
|
||||
"DeepL Pro Access": "Akses DeepL Pro",
|
||||
"Expanded Text-to-Speech Voices": "Suara Text-to-Speech yang Diperluas",
|
||||
"2000 MB Cloud Sync Space": "Ruang Sinkronisasi Cloud 2000 MB",
|
||||
"Includes All Plus Tier Benefits": "Termasuk Semua Manfaat Tingkat Plus",
|
||||
"Unlimited AI Summaries": "Ringkasan AI Tanpa Batas",
|
||||
"Unlimited AI Translations": "Terjemahan AI Tanpa Batas",
|
||||
"Unlimited DeepL Pro Access": "Akses DeepL Pro Tanpa Batas",
|
||||
"Advanced AI Tools": "Alat AI Lanjutan",
|
||||
"Early Feature Access": "Akses Fitur Awal",
|
||||
"10 GB Cloud Sync Space": "Ruang Sinkronisasi Cloud 10 GB",
|
||||
"Loading profile...": "Memuat profil...",
|
||||
"Features": "Fitur",
|
||||
"Delete Account": "Hapus Akun",
|
||||
"Delete Your Account?": "Hapus Akun Anda?",
|
||||
"This action cannot be undone. All your data in the cloud will be permanently deleted.": "Tindakan ini tidak dapat dibatalkan. Semua data Anda di cloud akan dihapus secara permanen.",
|
||||
"Delete Permanently": "Hapus Secara Permanen",
|
||||
"Free Tier": "Tingkat Gratis",
|
||||
"Plus Tier": "Tingkat Plus",
|
||||
"Pro Tier": "Tingkat Pro",
|
||||
"RTL Direction": "Arah RTL",
|
||||
"Maximum Column Height": "Tinggi Kolom Maksimum",
|
||||
"Maximum Column Width": "Lebar Kolom Maksimum",
|
||||
@@ -266,13 +236,13 @@
|
||||
"Clear Search": "Hapus Pencarian",
|
||||
"Header & Footer": "Header & Footer",
|
||||
"Apply also in Scrolled Mode": "Terapkan juga di Mode Gulir",
|
||||
"A new version of Readest is Available!": "Versi baru Readest Tersedia!",
|
||||
"A new version of Readest is available!": "Versi baru Readest Tersedia!",
|
||||
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} tersedia (versi terinstal {{currentVersion}}).",
|
||||
"Download and install now?": "Unduh dan instal sekarang?",
|
||||
"Downloading {{downloaded}} of {{contentLength}}": "Mengunduh {{downloaded}} dari {{contentLength}}",
|
||||
"Download finished": "Unduhan selesai",
|
||||
"DOWNLOAD & INSTALL": "UNDUH & INSTAL",
|
||||
"Changelog:": "Catatan Perubahan:",
|
||||
"Changelog": "Catatan Perubahan",
|
||||
"Software Update": "Pembaruan Perangkat Lunak",
|
||||
"Title": "Judul",
|
||||
"Date Read": "Tanggal Dibaca",
|
||||
@@ -281,10 +251,9 @@
|
||||
"Ascending": "Naik",
|
||||
"Descending": "Turun",
|
||||
"Sort by...": "Urutkan berdasarkan...",
|
||||
"Added:": "Ditambahkan:",
|
||||
"Format:": "Format:",
|
||||
"Added": "Ditambahkan",
|
||||
"GuanKiapTsingKhai-T": "GuanKiapTsingKhai-T",
|
||||
"Description:": "Deskripsi:",
|
||||
"Description": "Deskripsi",
|
||||
"No description available": "Tidak ada deskripsi yang tersedia",
|
||||
"List": "Daftar",
|
||||
"Grid": "Jala",
|
||||
@@ -301,7 +270,7 @@
|
||||
"Checking for updates...": "Memeriksa pembaruan...",
|
||||
"Error checking for updates": "Kesalahan saat memeriksa pembaruan",
|
||||
"Details": "Detail",
|
||||
"File Size:": "Ukuran File:",
|
||||
"File Size": "Ukuran File",
|
||||
"Auto Detect": "Deteksi Otomatis",
|
||||
"Next Section": "Bagian Selanjutnya",
|
||||
"Previous Section": "Bagian Sebelumnya",
|
||||
@@ -340,14 +309,12 @@
|
||||
"Synced at {{time}}": "Disinkronkan pada {{time}}",
|
||||
"Never synced": "Belum pernah disinkronkan",
|
||||
"Show Remaining Time": "Perlihatkan Waktu Tersisa",
|
||||
"Show Page Number": "Perlihatkan Nomor Halaman",
|
||||
"{{time}} min left in chapter": "{{time}} menit tersisa di bab",
|
||||
"Override Book Color": "Ubah Warna Buku",
|
||||
"Login Required": "Perlu masuk",
|
||||
"Quota Exceeded": "Kuota terlampaui",
|
||||
"{{percentage}}% of Daily Translation Characters Used.": "{{percentage}}% dari Karakter Terjemahan Harian Digunakan.",
|
||||
"Translation Characters": "Karakter Terjemahan",
|
||||
"Daily translation quota reached. Select another translate service to proceed.": "Kuota terjemahan harian tercapai. Pilih layanan terjemahan lain untuk melanjutkan.",
|
||||
"{{engine}}: {{count}} voices_other": "{{engine}}: {{count}} suara",
|
||||
"Something went wrong. Don't worry, our team has been notified and we're working on a fix.": "Terjadi kesalahan. Jangan khawatir, tim kami telah diberitahu dan kami sedang bekerja untuk memperbaikinya.",
|
||||
"Error Details:": "Rincian Kesalahan:",
|
||||
@@ -356,5 +323,182 @@
|
||||
"Contact Support": "Hubungi Dukungan",
|
||||
"Code Highlighting": "Penyorotan Kode",
|
||||
"Enable Highlighting": "Aktifkan Penyorotan",
|
||||
"Code Language": "Bahasa Kode"
|
||||
"Code Language": "Bahasa Kode",
|
||||
"Top Margin (px)": "Margin Atas (px)",
|
||||
"Bottom Margin (px)": "Margin Bawah (px)",
|
||||
"Right Margin (px)": "Margin Kanan (px)",
|
||||
"Left Margin (px)": "Margin Kiri (px)",
|
||||
"Column Gap (%)": "Jarak Antar Kolom (%)",
|
||||
"Always Show Status Bar": "Selalu Tampilkan Bilah Status",
|
||||
"Translation Not Available": "Terjemahan Tidak Tersedia",
|
||||
"Custom Content CSS": "CSS konten",
|
||||
"Enter CSS for book content styling...": "Masukkan CSS untuk gaya konten buku...",
|
||||
"Custom Reader UI CSS": "CSS antarmuka",
|
||||
"Enter CSS for reader interface styling...": "Masukkan CSS untuk gaya antarmuka pembaca...",
|
||||
"Crop": "Potong",
|
||||
"Book Covers": "Sampul Buku",
|
||||
"Fit": "Pas",
|
||||
"Reset {{settings}}": "Atur Ulang {{settings}}",
|
||||
"Reset Settings": "Atur Ulang Pengaturan",
|
||||
"{{count}} pages left in chapter_other": "{{count}} halaman tersisa di bab ini",
|
||||
"Show Remaining Pages": "Tampilkan halaman tersisa",
|
||||
"Source Han Serif CN VF": "Source Han Serif",
|
||||
"Huiwen-mincho": "Huiwen Mincho",
|
||||
"KingHwa_OldSong": "KingHwa Song",
|
||||
"Manage Subscription": "Kelola Langganan",
|
||||
"Coming Soon": "Segera Hadir",
|
||||
"Upgrade to {{plan}}": "Tingkatkan ke {{plan}}",
|
||||
"Upgrade to Plus or Pro": "Tingkatkan ke Plus atau Pro",
|
||||
"Current Plan": "Paket Saat Ini",
|
||||
"Plan Limits": "Batas Paket",
|
||||
"Processing your payment...": "Memproses pembayaran Anda...",
|
||||
"Please wait while we confirm your subscription.": "Mohon tunggu saat kami mengonfirmasi langganan Anda.",
|
||||
"Payment Processing": "Memproses Pembayaran",
|
||||
"Your payment is being processed. This usually takes a few moments.": "Pembayaran Anda sedang diproses. Ini biasanya memerlukan beberapa saat.",
|
||||
"Payment Failed": "Pembayaran Gagal",
|
||||
"We couldn't process your subscription. Please try again or contact support if the issue persists.": "Kami tidak dapat memproses langganan Anda. Silakan coba lagi atau hubungi dukungan jika masalah berlanjut.",
|
||||
"Back to Profile": "Kembali ke Profil",
|
||||
"Subscription Successful!": "Langganan Berhasil!",
|
||||
"Thank you for your subscription! Your payment has been processed successfully.": "Terima kasih atas langganan Anda! Pembayaran Anda telah berhasil diproses.",
|
||||
"Email:": "Email:",
|
||||
"Plan:": "Paket:",
|
||||
"Amount:": "Jumlah:",
|
||||
"Subscription ID:": "ID Langganan:",
|
||||
"Go to Library": "Pergi ke Perpustakaan",
|
||||
"Need help? Contact our support team at support@readest.com": "Perlu bantuan? Hubungi tim dukungan kami di support@readest.com",
|
||||
"Free Plan": "Paket Gratis",
|
||||
"month": "bulan",
|
||||
"AI Translations (per day)": "Terjemahan AI (per hari)",
|
||||
"Plus Plan": "Paket Plus",
|
||||
"Includes All Free Plan Benefits": "Termasuk Semua Manfaat Paket Gratis",
|
||||
"Pro Plan": "Paket Pro",
|
||||
"More AI Translations": "Lebih Banyak Terjemahan AI",
|
||||
"Complete Your Subscription": "Selesaikan Langganan Anda",
|
||||
"{{percentage}}% of Cloud Sync Space Used.": "{{percentage}}% ruang sinkronisasi cloud terpakai",
|
||||
"Cloud Sync Storage": "Penyimpanan Sinkronisasi Cloud",
|
||||
"Disable": "Nonaktifkan",
|
||||
"Enable": "Aktifkan",
|
||||
"Upgrade to Readest Premium": "Tingkatkan ke Readest Premium",
|
||||
"Show Source Text": "Perlihatkan Teks Sumber",
|
||||
"Cross-Platform Sync": "Sinkronisasi Lintas Platform",
|
||||
"Seamlessly sync your library, progress, highlights, and notes across all your devices—never lose your place again.": "Sinkronkan pustaka, kemajuan, dan catatan di semua perangkat Anda.",
|
||||
"Customizable Reading": "Membaca yang Dapat Disesuaikan",
|
||||
"Personalize every detail with adjustable fonts, layouts, themes, and advanced display settings for the perfect reading experience.": "Atur font, tata letak, dan tema sesuai keinginan.",
|
||||
"AI Read Aloud": "Pembacaan AI",
|
||||
"Enjoy hands-free reading with natural-sounding AI voices that bring your books to life.": "Dengarkan buku dengan suara AI alami.",
|
||||
"AI Translations": "Terjemahan AI",
|
||||
"Connect with fellow readers and get help fast in our friendly community channels.": "Terhubung dengan komunitas dan dapatkan bantuan cepat.",
|
||||
"Unlimited AI Read Aloud Hours": "Waktu Baca AI Tanpa Batas",
|
||||
"Listen without limits—convert as much text as you like into immersive audio.": "Dengarkan sebanyak yang Anda mau tanpa batas.",
|
||||
"Unlock enhanced translation capabilities with more daily usage and advanced options.": "Dapatkan lebih banyak kuota dan fitur terjemahan lanjutan.",
|
||||
"DeepL Pro Access": "Akses DeepL Pro",
|
||||
"Enjoy faster responses and dedicated assistance whenever you need help.": "Nikmati dukungan prioritas kapan saja.",
|
||||
"Daily translation quota reached. Upgrade your plan to continue using AI translations.": "Kuota harian habis. Tingkatkan paket Anda untuk lanjut.",
|
||||
"Translate any text instantly with the power of Google, Azure, or DeepL—understand content in any language.": "Terjemahkan teks apa pun secara instan dengan kekuatan Google, Azure, atau DeepL—pahami konten dalam bahasa apa pun.",
|
||||
"Includes All Plus Plan Benefits": "Termasuk Semua Manfaat Paket Plus",
|
||||
"Early Feature Access": "Akses Fitur Lebih Awal",
|
||||
"Be the first to explore new features, updates, and innovations before anyone else.": "Jadi yang pertama menjelajahi fitur baru, pembaruan, dan inovasi.",
|
||||
"Advanced AI Tools": "Alat AI Canggih",
|
||||
"Harness powerful AI tools for smarter reading, translation, and content discovery.": "Manfaatkan alat AI canggih untuk membaca cerdas, terjemahan, dan penemuan konten.",
|
||||
"Translate up to 100,000 characters daily with the most accurate translation engine available.": "Terjemahkan hingga 100.000 karakter harian dengan mesin terjemahan paling akurat.",
|
||||
"Translate up to 500,000 characters daily with the most accurate translation engine available.": "Terjemahkan hingga 500.000 karakter harian dengan mesin terjemahan paling akurat.",
|
||||
"Securely store and access your entire reading collection with up to 5 GB of cloud storage.": "Simpan dan akses koleksi bacaan lengkap dengan aman menggunakan penyimpanan cloud 5 GB.",
|
||||
"Securely store and access your entire reading collection with up to 20 GB of cloud storage.": "Simpan dan akses koleksi bacaan lengkap dengan aman menggunakan penyimpanan cloud 20 GB.",
|
||||
"Deleted cloud backup of the book: {{title}}": "Cadangan cloud buku dihapus: {{title}}",
|
||||
"Are you sure to delete the cloud backup of the selected book?": "Apakah Anda yakin ingin menghapus cadangan cloud dari buku yang dipilih?",
|
||||
"What's New in Readest": "Yang Baru di Readest",
|
||||
"Enter book title": "Masukkan judul buku",
|
||||
"Subtitle": "Subjudul",
|
||||
"Enter book subtitle": "Masukkan subjudul buku",
|
||||
"Enter author name": "Masukkan nama penulis",
|
||||
"Series": "Seri",
|
||||
"Enter series name": "Masukkan nama seri",
|
||||
"Series Index": "Nomor seri",
|
||||
"Enter series index": "Masukkan nomor seri",
|
||||
"Total in Series": "Total dalam seri",
|
||||
"Enter total books in series": "Masukkan total buku dalam seri",
|
||||
"Enter publisher": "Masukkan penerbit",
|
||||
"Publication Date": "Tanggal terbit",
|
||||
"Identifier": "Pengidentifikasi",
|
||||
"Enter book description": "Masukkan deskripsi buku",
|
||||
"Change cover image": "Ubah gambar sampul",
|
||||
"Replace": "Ganti",
|
||||
"Unlock cover": "Buka kunci sampul",
|
||||
"Lock cover": "Kunci sampul",
|
||||
"Auto-Retrieve Metadata": "Ambil metadata otomatis",
|
||||
"Auto-Retrieve": "Ambil otomatis",
|
||||
"Unlock all fields": "Buka kunci semua field",
|
||||
"Unlock All": "Buka semua",
|
||||
"Lock all fields": "Kunci semua field",
|
||||
"Lock All": "Kunci semua",
|
||||
"Reset": "Reset",
|
||||
"Edit Metadata": "Edit metadata",
|
||||
"Locked": "Terkunci",
|
||||
"Select Metadata Source": "Pilih sumber metadata",
|
||||
"Keep manual input": "Pertahankan input manual",
|
||||
"Google Books": "Google Books",
|
||||
"Open Library": "Open Library",
|
||||
"Fiction, Science, History": "Fiksi, Sains, Sejarah",
|
||||
"Select Cover Image": "Pilihan Gambar Sampul",
|
||||
"Open Book in New Window": "Buka Buku di Jendela Baru",
|
||||
"Voices for {{lang}}": "Suara untuk {{lang}}",
|
||||
"Yandex Translate": "Yandex Translate",
|
||||
"YYYY or YYYY-MM-DD": "YYYY atau YYYY-MM-DD",
|
||||
"Restore Purchase": "Pulihkan Pembelian",
|
||||
"No purchases found to restore.": "Tidak ada pembelian yang ditemukan untuk dipulihkan.",
|
||||
"Failed to restore purchases.": "Gagal memulihkan pembelian.",
|
||||
"Failed to manage subscription.": "Gagal mengelola langganan.",
|
||||
"Failed to load subscription plans.": "Gagal memuat rencana langganan.",
|
||||
"year": "tahun",
|
||||
"Failed to create checkout session": "Gagal membuat sesi checkout",
|
||||
"Storage": "Penyimpanan",
|
||||
"Terms of Service": "Ketentuan Layanan",
|
||||
"Privacy Policy": "Kebijakan Privasi",
|
||||
"Disable Double Click": "Nonaktifkan Klik Ganda",
|
||||
"TTS not supported for this document": "TTS tidak didukung untuk dokumen ini",
|
||||
"Reset Password": "Reset Kata Sandi",
|
||||
"Show Reading Progress": "Perlihatkan Progres Membaca",
|
||||
"Reading Progress Style": "Gaya Progres Membaca",
|
||||
"Page Number": "Nomor Halaman",
|
||||
"Percentage": "Persentase",
|
||||
"Deleted local copy of the book: {{title}}": "Salinan lokal buku dihapus: {{title}}",
|
||||
"Failed to delete cloud backup of the book: {{title}}": "Gagal menghapus cadangan cloud buku: {{title}}",
|
||||
"Failed to delete local copy of the book: {{title}}": "Gagal menghapus salinan lokal buku: {{title}}",
|
||||
"Are you sure to delete the local copy of the selected book?": "Apakah Anda yakin ingin menghapus salinan lokal buku yang dipilih?",
|
||||
"Remove from Cloud & Device": "Hapus dari Cloud & Perangkat",
|
||||
"Remove from Cloud Only": "Hapus dari Cloud Saja",
|
||||
"Remove from Device Only": "Hapus dari Perangkat Saja",
|
||||
"Disconnected": "Terputus",
|
||||
"KOReader Sync Settings": "Pengaturan Sinkronisasi KOReader",
|
||||
"Sync Strategy": "Strategi Sinkronisasi",
|
||||
"Ask on conflict": "Tanya saat terjadi konflik",
|
||||
"Always use latest": "Selalu gunakan yang terbaru",
|
||||
"Send changes only": "Kirim perubahan saja",
|
||||
"Receive changes only": "Terima perubahan saja",
|
||||
"Disabled": "Dinonaktifkan",
|
||||
"Checksum Method": "Metode Checksum",
|
||||
"File Content (recommended)": "Konten File (disarankan)",
|
||||
"File Name": "Nama File",
|
||||
"Device Name": "Nama Perangkat",
|
||||
"Sync Tolerance": "Toleransi Sinkronisasi",
|
||||
"Connect to your KOReader Sync server.": "Hubungkan ke server Sinkronisasi KOReader Anda.",
|
||||
"Server URL": "URL Server",
|
||||
"Username": "Nama Pengguna",
|
||||
"Your Username": "Nama Pengguna Anda",
|
||||
"Password": "Kata Sandi",
|
||||
"Connect": "Hubungkan",
|
||||
"KOReader Sync": "Sinkronisasi KOReader",
|
||||
"Sync Conflict": "Konflik Sinkronisasi",
|
||||
"Sync reading progress from \"{{deviceName}}\"?": "Sinkronkan progres membaca dari \"{{deviceName}}\"?",
|
||||
"another device": "perangkat lain",
|
||||
"Local Progress": "Progres Lokal",
|
||||
"Remote Progress": "Progres Jarak Jauh",
|
||||
"Page {{page}} of {{total}} ({{percentage}}%)": "Halaman {{page}} dari {{total}} ({{percentage}}%)",
|
||||
"Current position": "Posisi Saat Ini",
|
||||
"Approximately page {{page}} of {{total}} ({{percentage}}%)": "Sekitar halaman {{page}} dari {{total}} ({{percentage}}%)",
|
||||
"Approximately {{percentage}}%": "Sekitar {{percentage}}%",
|
||||
"Failed to connect": "Gagal terhubung",
|
||||
"Sync Server Connected": "Server sinkronisasi terhubung",
|
||||
"Precision: {{precision}} digits after the decimal": "Presisi: {{precision}} digit setelah desimal",
|
||||
"Sync as {{userDisplayName}}": "Sinkronkan sebagai {{userDisplayName}}"
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
"Confirm Deletion": "Conferma eliminazione",
|
||||
"Copied to notebook": "Copiato nel quaderno",
|
||||
"Copy": "Copia",
|
||||
"Custom CSS": "CSS personalizzato",
|
||||
"Dark Mode": "Modalità scura",
|
||||
"Default": "Predefinito",
|
||||
"Default Font": "Font predefinito",
|
||||
@@ -28,7 +27,6 @@
|
||||
"Dictionary": "Dizionario",
|
||||
"Download Readest": "Scarica Readest",
|
||||
"Edit": "Modifica",
|
||||
"Enter your custom CSS here...": "Inserisci qui il tuo CSS personalizzato...",
|
||||
"Excerpts": "Estratti",
|
||||
"Failed to import book(s): {{filenames}}": "Impossibile importare il/i libro/i: {{filenames}}",
|
||||
"Fast": "Veloce",
|
||||
@@ -48,7 +46,6 @@
|
||||
"Horizontal Direction": "Direzione orizzontale",
|
||||
"Hyphenation": "Sillabazione",
|
||||
"Import Books": "Importa libri",
|
||||
"Language:": "Lingua:",
|
||||
"Layout": "Layout",
|
||||
"Light Mode": "Modalità chiara",
|
||||
"Loading...": "Caricamento...",
|
||||
@@ -70,8 +67,8 @@
|
||||
"Paging Animation": "Animazione cambio pagina",
|
||||
"Paragraph": "Paragrafo",
|
||||
"Parallel Read": "Lettura parallela",
|
||||
"Published:": "Pubblicato:",
|
||||
"Publisher:": "Editore:",
|
||||
"Published": "Pubblicato",
|
||||
"Publisher": "Editore",
|
||||
"Reading Progress Synced": "Progresso lettura sincronizzato",
|
||||
"Reload Page": "Ricarica pagina",
|
||||
"Reveal in File Explorer": "Mostra in Esplora file",
|
||||
@@ -96,16 +93,15 @@
|
||||
"Slow": "Lento",
|
||||
"Solarized": "Solarizzato",
|
||||
"Speak": "Leggi",
|
||||
"Subjects:": "Argomenti:",
|
||||
"Subjects": "Argomenti",
|
||||
"System Fonts": "Font di sistema",
|
||||
"TTS not supported in this device": "TTS non supportato su questo dispositivo",
|
||||
"Theme Color": "Colore tema",
|
||||
"Theme Mode": "Modalità tema",
|
||||
"Translate": "Traduci",
|
||||
"Translated Text": "Testo tradotto",
|
||||
"Unknown": "Sconosciuto",
|
||||
"Untitled": "Senza titolo",
|
||||
"Updated:": "Aggiornato:",
|
||||
"Updated": "Aggiornato",
|
||||
"Version {{version}}": "Versione {{version}}",
|
||||
"Vertical Direction": "Direzione verticale",
|
||||
"Welcome to your library. You can import your books here and read them anytime.": "Benvenuto nella tua biblioteca. Puoi importare i tuoi libri qui e leggerli in qualsiasi momento.",
|
||||
@@ -114,8 +110,6 @@
|
||||
"Your Library": "La tua biblioteca",
|
||||
"TTS not supported for PDF": "TTS non supportato per PDF",
|
||||
"Override Book Font": "Sovrascrivi font libro",
|
||||
"Vertical Margins (px)": "Margini verticali (px)",
|
||||
"Horizontal Margins (%)": "Margini orizzontali (%)",
|
||||
"Apply to All Books": "Applica a tutti i libri",
|
||||
"Apply to This Book": "Applica a questo libro",
|
||||
"Unable to fetch the translation. Try again later.": "Impossibile recuperare la traduzione. Riprova più tardi.",
|
||||
@@ -130,8 +124,6 @@
|
||||
"Failed to download book: {{title}}": "Download libro non riuscito: {{title}}",
|
||||
"Upload Book": "Carica libro",
|
||||
"Auto Upload Books to Cloud": "Carica libri automaticamente su cloud",
|
||||
"{{percentage}}% of Cloud Storage Used.": "{{percentage}}% dello spazio di archiviazione cloud utilizzato.",
|
||||
"Storage": "Archiviazione",
|
||||
"Book deleted: {{title}}": "Libro eliminato: {{title}}",
|
||||
"Failed to delete book: {{title}}": "Eliminazione libro non riuscita: {{title}}",
|
||||
"Check Updates on Start": "Controlla aggiornamenti all'avvio",
|
||||
@@ -163,7 +155,7 @@
|
||||
"Create a Password": "Crea una password",
|
||||
"Sign up": "Registrati",
|
||||
"Signing up...": "Registrazione in corso...",
|
||||
"Don’t have an account? Sign up": "Non hai un account? Registrati",
|
||||
"Don't have an account? Sign up": "Non hai un account? Registrati",
|
||||
"Check your email for the confirmation link": "Controlla la tua email per il link di conferma",
|
||||
"Signing in ...": "Accesso in corso ...",
|
||||
"Send a magic link email": "Invia un'email con link magico",
|
||||
@@ -187,35 +179,13 @@
|
||||
"Sign in with GitHub": "Accedi con GitHub",
|
||||
"Account": "Account",
|
||||
"Failed to delete user. Please try again later.": "Impossibile eliminare l'utente. Riprova più tardi.",
|
||||
"Unlimited Offline/Online Reading": "Lettura offline/online illimitata",
|
||||
"Unlimited Cloud Sync Devices": "Sincronizzazione cloud su dispositivi illimitati",
|
||||
"Essential Text-to-Speech Voices": "Voci essenziali di sintesi vocale",
|
||||
"DeepL Free Access": "Accesso gratuito a DeepL",
|
||||
"Community Support": "Supporto della community",
|
||||
"500 MB Cloud Sync Space": "500 MB di spazio di sincronizzazione cloud",
|
||||
"Includes All Free Tier Benefits": "Include tutti i vantaggi del piano gratuito",
|
||||
"AI Summaries": "Riassunti AI",
|
||||
"AI Translations": "Traduzioni AI",
|
||||
"Priority Support": "Supporto prioritario",
|
||||
"DeepL Pro Access": "Accesso a DeepL Pro",
|
||||
"Expanded Text-to-Speech Voices": "Voci di sintesi vocale ampliate",
|
||||
"2000 MB Cloud Sync Space": "2000 MB di spazio di sincronizzazione cloud",
|
||||
"Includes All Plus Tier Benefits": "Include tutti i vantaggi del piano Plus",
|
||||
"Unlimited AI Summaries": "Riassunti AI illimitati",
|
||||
"Unlimited AI Translations": "Traduzioni AI illimitate",
|
||||
"Unlimited DeepL Pro Access": "Accesso illimitato a DeepL Pro",
|
||||
"Advanced AI Tools": "Strumenti AI avanzati",
|
||||
"Early Feature Access": "Accesso anticipato alle funzionalità",
|
||||
"10 GB Cloud Sync Space": "10 GB di spazio di sincronizzazione cloud",
|
||||
"Loading profile...": "Caricamento profilo...",
|
||||
"Features": "Funzionalità",
|
||||
"Delete Account": "Elimina account",
|
||||
"Delete Your Account?": "Eliminare il tuo account?",
|
||||
"This action cannot be undone. All your data in the cloud will be permanently deleted.": "Questa azione non può essere annullata. Tutti i tuoi dati nel cloud verranno eliminati definitivamente.",
|
||||
"Delete Permanently": "Elimina definitivamente",
|
||||
"Free Tier": "Piano gratuito",
|
||||
"Plus Tier": "Piano Plus",
|
||||
"Pro Tier": "Piano Pro",
|
||||
"RTL Direction": "Direzione RTL",
|
||||
"Maximum Column Height": "Altezza massima colonna",
|
||||
"Maximum Column Width": "Larghezza massima colonna",
|
||||
@@ -266,13 +236,13 @@
|
||||
"Clear Search": "Cancella ricerca",
|
||||
"Header & Footer": "Intestazione e piè di pagina",
|
||||
"Apply also in Scrolled Mode": "Applica anche in modalità scorrimento",
|
||||
"A new version of Readest is Available!": "È disponibile una nuova versione di Readest!",
|
||||
"A new version of Readest is available!": "È disponibile una nuova versione di Readest!",
|
||||
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} è disponibile (versione installata {{currentVersion}}).",
|
||||
"Download and install now?": "Scarica e installa ora?",
|
||||
"Downloading {{downloaded}} of {{contentLength}}": "Scaricando {{downloaded}} di {{contentLength}}",
|
||||
"Download finished": "Download completato",
|
||||
"DOWNLOAD & INSTALL": "SCARICA E INSTALLA",
|
||||
"Changelog:": "Note di rilascio:",
|
||||
"Changelog": "Note di rilascio",
|
||||
"Software Update": "Aggiornamento software",
|
||||
"Title": "Titolo",
|
||||
"Date Read": "Data di lettura",
|
||||
@@ -281,10 +251,9 @@
|
||||
"Ascending": "Crescente",
|
||||
"Descending": "Decrescente",
|
||||
"Sort by...": "Ordina per...",
|
||||
"Added:": "Aggiunto:",
|
||||
"Format:": "Formato:",
|
||||
"Added": "Aggiunto",
|
||||
"GuanKiapTsingKhai-T": "GuanKiapTsingKhai-T",
|
||||
"Description:": "Descrizione:",
|
||||
"Description": "Descrizione",
|
||||
"No description available": "Nessuna descrizione disponibile",
|
||||
"List": "Elenco",
|
||||
"Grid": "Griglia",
|
||||
@@ -301,7 +270,7 @@
|
||||
"Checking for updates...": "Controllo aggiornamenti...",
|
||||
"Error checking for updates": "Errore durante il controllo degli aggiornamenti",
|
||||
"Details": "Dettagli",
|
||||
"File Size:": "Dimensione file:",
|
||||
"File Size": "Dimensione file",
|
||||
"Auto Detect": "Rilevamento automatico",
|
||||
"Next Section": "Sezione successiva",
|
||||
"Previous Section": "Sezione precedente",
|
||||
@@ -344,14 +313,12 @@
|
||||
"Synced at {{time}}": "Sincronizzato alle {{time}}",
|
||||
"Never synced": "Mai sincronizzato",
|
||||
"Show Remaining Time": "Mostra tempo rimanente",
|
||||
"Show Page Number": "Mostra numero pagina",
|
||||
"{{time}} min left in chapter": "{{time}} min rimasti nel capitolo",
|
||||
"Override Book Color": "Override colore libro",
|
||||
"Login Required": "Accesso richiesto",
|
||||
"Quota Exceeded": "Limite superato",
|
||||
"{{percentage}}% of Daily Translation Characters Used.": "{{percentage}}% dei caratteri di traduzione giornalieri utilizzati.",
|
||||
"Translation Characters": "Caratteri di traduzione",
|
||||
"Daily translation quota reached. Select another translate service to proceed.": "Quota di traduzione giornaliera raggiunta. Seleziona un altro servizio di traduzione per procedere.",
|
||||
"{{engine}}: {{count}} voices_one": "{{engine}}: {{count}} voce",
|
||||
"{{engine}}: {{count}} voices_many": "{{engine}}: {{count}} voci",
|
||||
"{{engine}}: {{count}} voices_other": "{{engine}}: {{count}} voci",
|
||||
@@ -362,5 +329,184 @@
|
||||
"Contact Support": "Contatta il supporto",
|
||||
"Code Highlighting": "Evidenziazione codice",
|
||||
"Enable Highlighting": "Abilita evidenziazione",
|
||||
"Code Language": "Tipo di codice"
|
||||
"Code Language": "Tipo di codice",
|
||||
"Top Margin (px)": "Margine Superiore (px)",
|
||||
"Bottom Margin (px)": "Margine Inferiore (px)",
|
||||
"Right Margin (px)": "Margine Destro (px)",
|
||||
"Left Margin (px)": "Margine Sinistro (px)",
|
||||
"Column Gap (%)": "Spazio tra Colonne (%)",
|
||||
"Always Show Status Bar": "Mostra sempre la barra di stato",
|
||||
"Translation Not Available": "Traduzione non disponibile",
|
||||
"Custom Content CSS": "CSS contenuto",
|
||||
"Enter CSS for book content styling...": "Inserisci il CSS per il contenuto del libro...",
|
||||
"Custom Reader UI CSS": "CSS interfaccia",
|
||||
"Enter CSS for reader interface styling...": "Inserisci il CSS per l'interfaccia di lettura...",
|
||||
"Crop": "Ritaglia",
|
||||
"Book Covers": "Copertine libri",
|
||||
"Fit": "Adatta",
|
||||
"Reset {{settings}}": "Reimposta {{settings}}",
|
||||
"Reset Settings": "Reimposta impostazioni",
|
||||
"{{count}} pages left in chapter_one": "{{count}} pagina rimasta nel capitolo",
|
||||
"{{count}} pages left in chapter_many": "{{count}} pagine rimaste nel capitolo",
|
||||
"{{count}} pages left in chapter_other": "{{count}} pagine rimaste nel capitolo",
|
||||
"Show Remaining Pages": "Mostra pagine rimanenti",
|
||||
"Source Han Serif CN VF": "Source Han Serif",
|
||||
"Huiwen-mincho": "Huiwen Mincho",
|
||||
"KingHwa_OldSong": "KingHwa Song",
|
||||
"Manage Subscription": "Gestisci Abbonamento",
|
||||
"Coming Soon": "Prossimamente",
|
||||
"Upgrade to {{plan}}": "Aggiorna a {{plan}}",
|
||||
"Upgrade to Plus or Pro": "Aggiorna a Plus o Pro",
|
||||
"Current Plan": "Piano Attuale",
|
||||
"Plan Limits": "Limiti del Piano",
|
||||
"Processing your payment...": "Elaborazione del pagamento...",
|
||||
"Please wait while we confirm your subscription.": "Attendi mentre confermiamo il tuo abbonamento.",
|
||||
"Payment Processing": "Elaborazione Pagamento",
|
||||
"Your payment is being processed. This usually takes a few moments.": "Il tuo pagamento è in elaborazione. Di solito richiede pochi istanti.",
|
||||
"Payment Failed": "Pagamento Fallito",
|
||||
"We couldn't process your subscription. Please try again or contact support if the issue persists.": "Non è stato possibile elaborare l'abbonamento. Riprova o contatta il supporto se il problema persiste.",
|
||||
"Back to Profile": "Torna al Profilo",
|
||||
"Subscription Successful!": "Abbonamento Avvenuto con Successo!",
|
||||
"Thank you for your subscription! Your payment has been processed successfully.": "Grazie per l'abbonamento! Il pagamento è stato elaborato con successo.",
|
||||
"Email:": "Email:",
|
||||
"Plan:": "Piano:",
|
||||
"Amount:": "Importo:",
|
||||
"Subscription ID:": "ID Abbonamento:",
|
||||
"Go to Library": "Vai alla Libreria",
|
||||
"Need help? Contact our support team at support@readest.com": "Hai bisogno di aiuto? Contatta il nostro supporto a support@readest.com",
|
||||
"Free Plan": "Piano Gratuito",
|
||||
"month": "mese",
|
||||
"AI Translations (per day)": "Traduzioni AI (al giorno)",
|
||||
"Plus Plan": "Piano Plus",
|
||||
"Includes All Free Plan Benefits": "Include Tutti i Vantaggi del Piano Gratuito",
|
||||
"Pro Plan": "Piano Pro",
|
||||
"More AI Translations": "Più Traduzioni AI",
|
||||
"Complete Your Subscription": "Completa l'abbonamento",
|
||||
"{{percentage}}% of Cloud Sync Space Used.": "{{percentage}}% dello spazio cloud usato",
|
||||
"Cloud Sync Storage": "Spazio cloud",
|
||||
"Disable": "Disabilita",
|
||||
"Enable": "Abilita",
|
||||
"Upgrade to Readest Premium": "Aggiorna a Readest Premium",
|
||||
"Show Source Text": "Mostra Testo Originale",
|
||||
"Cross-Platform Sync": "Sincronizzazione Multipiattaforma",
|
||||
"Seamlessly sync your library, progress, highlights, and notes across all your devices—never lose your place again.": "Sincronizza libreria, progressi e note su tutti i tuoi dispositivi.",
|
||||
"Customizable Reading": "Lettura Personalizzabile",
|
||||
"Personalize every detail with adjustable fonts, layouts, themes, and advanced display settings for the perfect reading experience.": "Personalizza font, layout e temi come preferisci.",
|
||||
"AI Read Aloud": "Lettura AI",
|
||||
"Enjoy hands-free reading with natural-sounding AI voices that bring your books to life.": "Ascolta i libri con voci AI naturali.",
|
||||
"AI Translations": "Traduzioni AI",
|
||||
"Connect with fellow readers and get help fast in our friendly community channels.": "Connettiti con la community e ricevi supporto rapido.",
|
||||
"Unlimited AI Read Aloud Hours": "Lettura AI Illimitata",
|
||||
"Listen without limits—convert as much text as you like into immersive audio.": "Ascolta senza limiti quanto vuoi.",
|
||||
"Unlock enhanced translation capabilities with more daily usage and advanced options.": "Sblocca traduzioni avanzate e più utilizzo giornaliero.",
|
||||
"DeepL Pro Access": "Accesso DeepL Pro",
|
||||
"Enjoy faster responses and dedicated assistance whenever you need help.": "Ricevi supporto prioritario quando serve.",
|
||||
"Daily translation quota reached. Upgrade your plan to continue using AI translations.": "Quota giornaliera esaurita. Passa a un piano superiore per continuare.",
|
||||
"Translate any text instantly with the power of Google, Azure, or DeepL—understand content in any language.": "Traduci qualsiasi testo istantaneamente con la potenza di Google, Azure o DeepL—comprendi contenuti in qualsiasi lingua.",
|
||||
"Includes All Plus Plan Benefits": "Include Tutti i Vantaggi del Piano Plus",
|
||||
"Early Feature Access": "Accesso Anticipato alle Funzionalità",
|
||||
"Be the first to explore new features, updates, and innovations before anyone else.": "Esplora per primo nuove funzionalità, aggiornamenti e innovazioni.",
|
||||
"Advanced AI Tools": "Strumenti AI Avanzati",
|
||||
"Harness powerful AI tools for smarter reading, translation, and content discovery.": "Sfrutta potenti strumenti AI per lettura intelligente, traduzione e scoperta di contenuti.",
|
||||
"Translate up to 100,000 characters daily with the most accurate translation engine available.": "Traduci fino a 100.000 caratteri al giorno con il motore di traduzione più accurato.",
|
||||
"Translate up to 500,000 characters daily with the most accurate translation engine available.": "Traduci fino a 500.000 caratteri al giorno con il motore di traduzione più accurato.",
|
||||
"Securely store and access your entire reading collection with up to 5 GB of cloud storage.": "Archivia e accedi in sicurezza alla tua intera collezione di lettura con fino a 5 GB di storage cloud.",
|
||||
"Securely store and access your entire reading collection with up to 20 GB of cloud storage.": "Archivia e accedi in sicurezza alla tua intera collezione di lettura con fino a 20 GB di storage cloud.",
|
||||
"Deleted cloud backup of the book: {{title}}": "Backup cloud del libro eliminato: {{title}}",
|
||||
"Are you sure to delete the cloud backup of the selected book?": "Sei sicuro di voler eliminare il backup cloud del libro selezionato?",
|
||||
"What's New in Readest": "Novità in Readest",
|
||||
"Enter book title": "Inserisci il titolo del libro",
|
||||
"Subtitle": "Sottotitolo",
|
||||
"Enter book subtitle": "Inserisci il sottotitolo del libro",
|
||||
"Enter author name": "Inserisci il nome dell'autore",
|
||||
"Series": "Serie",
|
||||
"Enter series name": "Inserisci il nome della serie",
|
||||
"Series Index": "Numero della serie",
|
||||
"Enter series index": "Inserisci il numero della serie",
|
||||
"Total in Series": "Totale nella serie",
|
||||
"Enter total books in series": "Inserisci il numero totale di libri nella serie",
|
||||
"Enter publisher": "Inserisci l'editore",
|
||||
"Publication Date": "Data di pubblicazione",
|
||||
"Identifier": "Identificatore",
|
||||
"Enter book description": "Inserisci la descrizione del libro",
|
||||
"Change cover image": "Cambia immagine di copertina",
|
||||
"Replace": "Sostituisci",
|
||||
"Unlock cover": "Sblocca copertina",
|
||||
"Lock cover": "Blocca copertina",
|
||||
"Auto-Retrieve Metadata": "Recupera metadati automaticamente",
|
||||
"Auto-Retrieve": "Recupero automatico",
|
||||
"Unlock all fields": "Sblocca tutti i campi",
|
||||
"Unlock All": "Sblocca tutto",
|
||||
"Lock all fields": "Blocca tutti i campi",
|
||||
"Lock All": "Blocca tutto",
|
||||
"Reset": "Ripristina",
|
||||
"Edit Metadata": "Modifica metadati",
|
||||
"Locked": "Bloccato",
|
||||
"Select Metadata Source": "Seleziona fonte metadati",
|
||||
"Keep manual input": "Mantieni input manuale",
|
||||
"Google Books": "Google Books",
|
||||
"Open Library": "Open Library",
|
||||
"Fiction, Science, History": "Narrativa, Scienza, Storia",
|
||||
"Select Cover Image": "Seleziona immagine di copertina",
|
||||
"Open Book in New Window": "Apri libro in una nuova finestra",
|
||||
"Voices for {{lang}}": "Voci per {{lang}}",
|
||||
"Yandex Translate": "Yandex Translate",
|
||||
"YYYY or YYYY-MM-DD": "AAAA o AAAA-MM-GG",
|
||||
"Restore Purchase": "Ripristina acquisto",
|
||||
"No purchases found to restore.": "Nessun acquisto trovato da ripristinare.",
|
||||
"Failed to restore purchases.": "Impossibile ripristinare gli acquisti.",
|
||||
"Failed to manage subscription.": "Impossibile gestire l'abbonamento.",
|
||||
"Failed to load subscription plans.": "Impossibile caricare i piani di abbonamento.",
|
||||
"year": "anno",
|
||||
"Failed to create checkout session": "Impossibile creare la sessione di checkout",
|
||||
"Storage": "Archiviazione",
|
||||
"Terms of Service": "Termini di servizio",
|
||||
"Privacy Policy": "Informativa sulla privacy",
|
||||
"Disable Double Click": "Disabilita Doppio Clic",
|
||||
"TTS not supported for this document": "TTS non supportato per questo documento",
|
||||
"Reset Password": "Reimposta Password",
|
||||
"Show Reading Progress": "Mostra Progresso Lettura",
|
||||
"Reading Progress Style": "Stile di Progresso Lettura",
|
||||
"Page Number": "Numero di Pagina",
|
||||
"Percentage": "Percentuale",
|
||||
"Deleted local copy of the book: {{title}}": "Copie locale del libro eliminata: {{title}}",
|
||||
"Failed to delete cloud backup of the book: {{title}}": "Impossibile eliminare il backup cloud del libro: {{title}}",
|
||||
"Failed to delete local copy of the book: {{title}}": "Impossibile eliminare la copia locale del libro: {{title}}",
|
||||
"Are you sure to delete the local copy of the selected book?": "Sei sicuro di voler eliminare la copia locale del libro selezionato?",
|
||||
"Remove from Cloud & Device": "Rimuovi da Cloud & Dispositivo",
|
||||
"Remove from Cloud Only": "Rimuovi solo da Cloud",
|
||||
"Remove from Device Only": "Rimuovi solo da Dispositivo",
|
||||
"Disconnected": "Disconnesso",
|
||||
"KOReader Sync Settings": "Impostazioni di Sincronizzazione KOReader",
|
||||
"Sync Strategy": "Strategia di Sincronizzazione",
|
||||
"Ask on conflict": "Chiedi in caso di conflitto",
|
||||
"Always use latest": "Usa sempre l'ultima versione",
|
||||
"Send changes only": "Invia solo le modifiche",
|
||||
"Receive changes only": "Ricevi solo le modifiche",
|
||||
"Disabled": "Disabilitato",
|
||||
"Checksum Method": "Metodo di Controllo",
|
||||
"File Content (recommended)": "Contenuto del File (consigliato)",
|
||||
"File Name": "Nome del File",
|
||||
"Device Name": "Nome del Dispositivo",
|
||||
"Sync Tolerance": "Tolleranza di Sincronizzazione",
|
||||
"Connect to your KOReader Sync server.": "Connettiti al tuo server KOReader Sync.",
|
||||
"Server URL": "URL del Server",
|
||||
"Username": "Nome Utente",
|
||||
"Your Username": "Il tuo Nome Utente",
|
||||
"Password": "Password",
|
||||
"Connect": "Connetti",
|
||||
"KOReader Sync": "KOReader Sync",
|
||||
"Sync Conflict": "Conflitto di Sincronizzazione",
|
||||
"Sync reading progress from \"{{deviceName}}\"?": "Sincronizzare il progresso di lettura da \"{{deviceName}}\"?",
|
||||
"another device": "un altro dispositivo",
|
||||
"Local Progress": "Progresso Locale",
|
||||
"Remote Progress": "Progresso Remoto",
|
||||
"Page {{page}} of {{total}} ({{percentage}}%)": "Pagina {{page}} di {{total}} ({{percentage}}%)",
|
||||
"Current position": "Posizione Corrente",
|
||||
"Approximately page {{page}} of {{total}} ({{percentage}}%)": "Circa pagina {{page}} di {{total}} ({{percentage}}%)",
|
||||
"Approximately {{percentage}}%": "Circa {{percentage}}%",
|
||||
"Failed to connect": "Impossibile connettersi",
|
||||
"Sync Server Connected": "Server di sincronizzazione connesso",
|
||||
"Precision: {{precision}} digits after the decimal": "Precisione: {{precision}} cifre dopo la virgola",
|
||||
"Sync as {{userDisplayName}}": "Sincronizza come {{userDisplayName}}"
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
"Confirm Deletion": "削除の確認",
|
||||
"Copied to notebook": "ノートブックにコピー",
|
||||
"Copy": "コピー",
|
||||
"Custom CSS": "カスタムCSS",
|
||||
"Dark Mode": "ダークモード",
|
||||
"Default": "デフォルト",
|
||||
"Default Font": "デフォルトフォント",
|
||||
@@ -28,7 +27,6 @@
|
||||
"Dictionary": "辞書",
|
||||
"Download Readest": "Readestをダウンロード",
|
||||
"Edit": "編集",
|
||||
"Enter your custom CSS here...": "ここにカスタムCSSを入力...",
|
||||
"Excerpts": "抜粋",
|
||||
"Failed to import book(s): {{filenames}}": "書籍のインポートに失敗しました:{{filenames}}",
|
||||
"Fast": "高速",
|
||||
@@ -48,7 +46,6 @@
|
||||
"Horizontal Direction": "横書",
|
||||
"Hyphenation": "ハイフネーション",
|
||||
"Import Books": "書籍をインポート",
|
||||
"Language:": "言語:",
|
||||
"Layout": "レイアウト",
|
||||
"Light Mode": "ライトモード",
|
||||
"Loading...": "読み込み中...",
|
||||
@@ -70,8 +67,8 @@
|
||||
"Paging Animation": "ページめくりアニメーション",
|
||||
"Paragraph": "段落",
|
||||
"Parallel Read": "並列読書",
|
||||
"Published:": "出版日:",
|
||||
"Publisher:": "出版社:",
|
||||
"Published": "出版日",
|
||||
"Publisher": "出版社",
|
||||
"Reading Progress Synced": "読書進捗が同期されました",
|
||||
"Reload Page": "ページを再読み込み",
|
||||
"Reveal in File Explorer": "エクスプローラーで表示",
|
||||
@@ -96,16 +93,15 @@
|
||||
"Slow": "低速",
|
||||
"Solarized": "ソーラライズド",
|
||||
"Speak": "読み上げ",
|
||||
"Subjects:": "主題:",
|
||||
"Subjects": "主題",
|
||||
"System Fonts": "システムフォント",
|
||||
"TTS not supported in this device": "このデバイスではTTSがサポートされていません",
|
||||
"Theme Color": "テーマカラー",
|
||||
"Theme Mode": "テーマモード",
|
||||
"Translate": "翻訳",
|
||||
"Translated Text": "翻訳されたテキスト",
|
||||
"Unknown": "不明",
|
||||
"Untitled": "無題",
|
||||
"Updated:": "更新日:",
|
||||
"Updated": "更新日",
|
||||
"Version {{version}}": "バージョン {{version}}",
|
||||
"Vertical Direction": "縦書",
|
||||
"Welcome to your library. You can import your books here and read them anytime.": "ライブラリーへようこそ。ここに書籍をインポートして、いつでも読むことができます。",
|
||||
@@ -114,8 +110,6 @@
|
||||
"Your Library": "ライブラリー",
|
||||
"TTS not supported for PDF": "PDFではTTSがサポートされていません",
|
||||
"Override Book Font": "書籍フォントを上書き",
|
||||
"Vertical Margins (px)": "縦マージン(px)",
|
||||
"Horizontal Margins (%)": "横マージン(%)",
|
||||
"Apply to All Books": "すべての書籍に適用",
|
||||
"Apply to This Book": "この書籍に適用",
|
||||
"Unable to fetch the translation. Try again later.": "翻訳を取得できません。後で再試行してください。",
|
||||
@@ -130,8 +124,6 @@
|
||||
"Failed to download book: {{title}}": "書籍のダウンロードに失敗しました:{{title}}",
|
||||
"Upload Book": "書籍をアップロード",
|
||||
"Auto Upload Books to Cloud": "書籍を自動的にクラウドにアップロード",
|
||||
"{{percentage}}% of Cloud Storage Used.": "クラウドストレージの{{percentage}}%が使用されています。",
|
||||
"Storage": "ストレージ",
|
||||
"Book deleted: {{title}}": "書籍が削除されました:{{title}}",
|
||||
"Failed to delete book: {{title}}": "書籍の削除に失敗しました:{{title}}",
|
||||
"Check Updates on Start": "開始時に更新を確認",
|
||||
@@ -163,7 +155,7 @@
|
||||
"Create a Password": "パスワードを作成",
|
||||
"Sign up": "サインアップ",
|
||||
"Signing up...": "サインアップ中...",
|
||||
"Don’t have an account? Sign up": "アカウントをお持ちでないですか? サインアップ",
|
||||
"Don't have an account? Sign up": "アカウントをお持ちでないですか? サインアップ",
|
||||
"Check your email for the confirmation link": "確認リンクのためにメールを確認してください",
|
||||
"Signing in ...": "サインイン中 ...",
|
||||
"Send a magic link email": "マジックリンクメールを送信",
|
||||
@@ -187,35 +179,13 @@
|
||||
"Sign in with GitHub": "GitHubでサインイン",
|
||||
"Account": "アカウント",
|
||||
"Failed to delete user. Please try again later.": "ユーザーの削除に失敗しました。後ほど再試行してください。",
|
||||
"Unlimited Offline/Online Reading": "無制限のオフライン/オンライン読書",
|
||||
"Unlimited Cloud Sync Devices": "無制限のクラウド同期デバイス",
|
||||
"Essential Text-to-Speech Voices": "基本的な音声合成ボイス",
|
||||
"DeepL Free Access": "DeepL無料アクセス",
|
||||
"Community Support": "コミュニティサポート",
|
||||
"500 MB Cloud Sync Space": "500 MBのクラウド同期スペース",
|
||||
"Includes All Free Tier Benefits": "無料プランの全特典を含む",
|
||||
"AI Summaries": "AI要約",
|
||||
"AI Translations": "AI翻訳",
|
||||
"Priority Support": "優先サポート",
|
||||
"DeepL Pro Access": "DeepL Proアクセス",
|
||||
"Expanded Text-to-Speech Voices": "拡張された音声合成ボイス",
|
||||
"2000 MB Cloud Sync Space": "2000 MBのクラウド同期スペース",
|
||||
"Includes All Plus Tier Benefits": "Plusプランの全特典を含む",
|
||||
"Unlimited AI Summaries": "無制限のAI要約",
|
||||
"Unlimited AI Translations": "無制限のAI翻訳",
|
||||
"Unlimited DeepL Pro Access": "無制限のDeepL Proアクセス",
|
||||
"Advanced AI Tools": "高度なAIツール",
|
||||
"Early Feature Access": "新機能への早期アクセス",
|
||||
"10 GB Cloud Sync Space": "10 GBのクラウド同期スペース",
|
||||
"Loading profile...": "プロフィール読み込み中...",
|
||||
"Features": "機能",
|
||||
"Delete Account": "アカウント削除",
|
||||
"Delete Your Account?": "アカウントを削除しますか?",
|
||||
"This action cannot be undone. All your data in the cloud will be permanently deleted.": "この操作は元に戻せません。クラウド上のすべてのデータが完全に削除されます。",
|
||||
"Delete Permanently": "完全に削除",
|
||||
"Free Tier": "無料プラン",
|
||||
"Plus Tier": "Plusプラン",
|
||||
"Pro Tier": "Proプラン",
|
||||
"RTL Direction": "RTL方向",
|
||||
"Maximum Column Height": "最大列高",
|
||||
"Maximum Column Width": "最大列幅",
|
||||
@@ -266,13 +236,13 @@
|
||||
"Clear Search": "検索をクリア",
|
||||
"Header & Footer": "ヘッダーとフッター",
|
||||
"Apply also in Scrolled Mode": "スクロールモードでも適用",
|
||||
"A new version of Readest is Available!": "新しいバージョンのReadestが利用可能です!",
|
||||
"A new version of Readest is available!": "新しいバージョンのReadestが利用可能です!",
|
||||
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}}が利用可能です(インストールされたバージョン{{currentVersion}})。",
|
||||
"Download and install now?": "今すぐダウンロードしてインストールしますか?",
|
||||
"Downloading {{downloaded}} of {{contentLength}}": "ダウンロード中 {{downloaded}} / {{contentLength}}",
|
||||
"Download finished": "ダウンロード完了",
|
||||
"DOWNLOAD & INSTALL": "更新",
|
||||
"Changelog:": "変更ログ:",
|
||||
"Changelog": "変更ログ",
|
||||
"Software Update": "ソフトウェアの更新",
|
||||
"Title": "タイトル",
|
||||
"Date Read": "読書日",
|
||||
@@ -281,10 +251,9 @@
|
||||
"Ascending": "昇順",
|
||||
"Descending": "降順",
|
||||
"Sort by...": "並べ替え...",
|
||||
"Added:": "追加日:",
|
||||
"Format:": "形式:",
|
||||
"Added": "追加日",
|
||||
"GuanKiapTsingKhai-T": "GuanKiapTsingKhai-T",
|
||||
"Description:": "説明:",
|
||||
"Description": "説明",
|
||||
"No description available": "説明はありません",
|
||||
"List": "リスト",
|
||||
"Grid": "グリッド",
|
||||
@@ -301,7 +270,7 @@
|
||||
"Checking for updates...": "更新を確認中...",
|
||||
"Error checking for updates": "更新の確認中にエラーが発生しました",
|
||||
"Details": "詳細",
|
||||
"File Size:": "ファイルサイズ:",
|
||||
"File Size": "ファイルサイズ",
|
||||
"Auto Detect": "自動検出",
|
||||
"Next Section": "次のセクション",
|
||||
"Previous Section": "前のセクション",
|
||||
@@ -340,14 +309,12 @@
|
||||
"Synced at {{time}}": "{{time}} に同期済み",
|
||||
"Never synced": "まだ同期されていません",
|
||||
"Show Remaining Time": "残り時間を表示",
|
||||
"Show Page Number": "ページ番号を表示",
|
||||
"{{time}} min left in chapter": "{{time}}分残り",
|
||||
"Override Book Color": "書籍の色を上書き",
|
||||
"Login Required": "ログイン必須",
|
||||
"Quota Exceeded": "上限超過",
|
||||
"{{percentage}}% of Daily Translation Characters Used.": "日次翻訳文字数の使用量は{{percentage}}%です。",
|
||||
"Translation Characters": "翻訳文字数",
|
||||
"Daily translation quota reached. Select another translate service to proceed.": "日次翻訳クォータに達しました。続行するには別の翻訳サービスを選択してください。",
|
||||
"{{engine}}: {{count}} voices_other": "{{engine}}: {{count}}音声",
|
||||
"Something went wrong. Don't worry, our team has been notified and we're working on a fix.": "何か問題が発生しました。心配しないでください。私たちのチームが通知を受けており、修正に取り組んでいます。",
|
||||
"Error Details:": "エラーの詳細:",
|
||||
@@ -356,5 +323,182 @@
|
||||
"Contact Support": "サポートに連絡",
|
||||
"Code Highlighting": "コードハイライト",
|
||||
"Enable Highlighting": "ハイライトを有効にする",
|
||||
"Code Language": "コード言語"
|
||||
"Code Language": "コード言語",
|
||||
"Top Margin (px)": "上マージン (px)",
|
||||
"Bottom Margin (px)": "下マージン (px)",
|
||||
"Right Margin (px)": "右マージン (px)",
|
||||
"Left Margin (px)": "左マージン (px)",
|
||||
"Column Gap (%)": "列間の間隔 (%)",
|
||||
"Always Show Status Bar": "ステータスバーを常に表示",
|
||||
"Translation Not Available": "翻訳は利用できません",
|
||||
"Custom Content CSS": "コンテンツCSS",
|
||||
"Enter CSS for book content styling...": "本のコンテンツ用CSSを入力...",
|
||||
"Custom Reader UI CSS": "リーダーUIのCSS",
|
||||
"Enter CSS for reader interface styling...": "リーダーインターフェース用CSSを入力...",
|
||||
"Crop": "トリミング",
|
||||
"Book Covers": "書籍の表紙",
|
||||
"Fit": "フィット",
|
||||
"Reset {{settings}}": "{{settings}}をリセット",
|
||||
"Reset Settings": "設定をリセット",
|
||||
"{{count}} pages left in chapter_other": "{{count}} ページ残り",
|
||||
"Show Remaining Pages": "残りを表示",
|
||||
"Source Han Serif CN VF": "Source Han Serif",
|
||||
"Huiwen-mincho": "Huiwen Mincho",
|
||||
"KingHwa_OldSong": "KingHwa Song",
|
||||
"Manage Subscription": "サブスクリプションを管理",
|
||||
"Coming Soon": "近日公開",
|
||||
"Upgrade to {{plan}}": "{{plan}} にアップグレード",
|
||||
"Upgrade to Plus or Pro": "Plus または Pro にアップグレード",
|
||||
"Current Plan": "現在のプラン",
|
||||
"Plan Limits": "プランの上限",
|
||||
"Processing your payment...": "お支払いを処理中...",
|
||||
"Please wait while we confirm your subscription.": "サブスクリプションを確認しています。しばらくお待ちください。",
|
||||
"Payment Processing": "支払いを処理中",
|
||||
"Your payment is being processed. This usually takes a few moments.": "お支払いを処理しています。通常、数秒かかります。",
|
||||
"Payment Failed": "支払いに失敗しました",
|
||||
"We couldn't process your subscription. Please try again or contact support if the issue persists.": "サブスクリプションを処理できませんでした。再度お試しいただくか、問題が続く場合はサポートにお問い合わせください。",
|
||||
"Back to Profile": "プロフィールに戻る",
|
||||
"Subscription Successful!": "サブスクリプションが完了しました!",
|
||||
"Thank you for your subscription! Your payment has been processed successfully.": "ご登録ありがとうございます!お支払いが正常に処理されました。",
|
||||
"Email:": "メール:",
|
||||
"Plan:": "プラン:",
|
||||
"Amount:": "金額:",
|
||||
"Subscription ID:": "サブスクリプションID:",
|
||||
"Go to Library": "ライブラリへ移動",
|
||||
"Need help? Contact our support team at support@readest.com": "サポートが必要ですか? support@readest.com までお問い合わせください。",
|
||||
"Free Plan": "無料プラン",
|
||||
"month": "月",
|
||||
"AI Translations (per day)": "AI翻訳(1日あたり)",
|
||||
"Plus Plan": "Plusプラン",
|
||||
"Includes All Free Plan Benefits": "無料プランの全特典を含む",
|
||||
"Pro Plan": "Proプラン",
|
||||
"More AI Translations": "さらに多くのAI翻訳",
|
||||
"Complete Your Subscription": "サブスクリプションを完了する",
|
||||
"{{percentage}}% of Cloud Sync Space Used.": "クラウド同期スペースの使用量は{{percentage}}%です。",
|
||||
"Cloud Sync Storage": "クラウド同期容量",
|
||||
"Disable": "無効にする",
|
||||
"Enable": "有効にする",
|
||||
"Upgrade to Readest Premium": "Readest Premiumにアップグレード",
|
||||
"Show Source Text": "原文を表示",
|
||||
"Cross-Platform Sync": "クロスプラットフォーム同期",
|
||||
"Seamlessly sync your library, progress, highlights, and notes across all your devices—never lose your place again.": "ライブラリや進捗、メモを全デバイスで同期。",
|
||||
"Customizable Reading": "カスタマイズ可能な読書",
|
||||
"Personalize every detail with adjustable fonts, layouts, themes, and advanced display settings for the perfect reading experience.": "フォントやレイアウト、テーマを自由に調整。",
|
||||
"AI Read Aloud": "AI音声読み上げ",
|
||||
"Enjoy hands-free reading with natural-sounding AI voices that bring your books to life.": "自然なAI音声で本を聴く。",
|
||||
"AI Translations": "AI翻訳",
|
||||
"Translate any text instantly with the power of Google, Azure, or DeepL—understand content in any language.": "Google、Azure、DeepLで即時翻訳。",
|
||||
"Connect with fellow readers and get help fast in our friendly community channels.": "コミュニティで交流・サポートを利用。",
|
||||
"Unlimited AI Read Aloud Hours": "無制限のAI読み上げ",
|
||||
"Listen without limits—convert as much text as you like into immersive audio.": "制限なくテキストを音声化。",
|
||||
"Unlock enhanced translation capabilities with more daily usage and advanced options.": "高度な翻訳機能を利用可能に。",
|
||||
"DeepL Pro Access": "DeepL Pro利用",
|
||||
"Enjoy faster responses and dedicated assistance whenever you need help.": "優先サポートを利用可能。",
|
||||
"Daily translation quota reached. Upgrade your plan to continue using AI translations.": "翻訳上限に達しました。アップグレードで継続可能です。",
|
||||
"Includes All Plus Plan Benefits": "Plusプランの全特典を含む",
|
||||
"Early Feature Access": "新機能への先行アクセス",
|
||||
"Be the first to explore new features, updates, and innovations before anyone else.": "新機能、アップデート、革新を誰よりも先に体験。",
|
||||
"Advanced AI Tools": "高度なAIツール",
|
||||
"Harness powerful AI tools for smarter reading, translation, and content discovery.": "スマートな読書、翻訳、コンテンツ発見のための強力なAIツールを活用。",
|
||||
"Translate up to 100,000 characters daily with the most accurate translation engine available.": "最も正確な翻訳エンジンで1日最大10万文字を翻訳。",
|
||||
"Translate up to 500,000 characters daily with the most accurate translation engine available.": "最も正確な翻訳エンジンで1日最大50万文字を翻訳。",
|
||||
"Securely store and access your entire reading collection with up to 5 GB of cloud storage.": "最大5GBの安全なクラウドストレージで読書コレクション全体を保存・アクセス。",
|
||||
"Securely store and access your entire reading collection with up to 20 GB of cloud storage.": "最大20GBの安全なクラウドストレージで読書コレクション全体を保存・アクセス。",
|
||||
"Deleted cloud backup of the book: {{title}}": "書籍のクラウドバックアップを削除しました:{{title}}",
|
||||
"Are you sure to delete the cloud backup of the selected book?": "選択した書籍のクラウドバックアップを削除してもよろしいですか?",
|
||||
"What's New in Readest": "Readestの新機能",
|
||||
"Enter book title": "本のタイトルを入力",
|
||||
"Subtitle": "サブタイトル",
|
||||
"Enter book subtitle": "本のサブタイトルを入力",
|
||||
"Enter author name": "著者名を入力",
|
||||
"Series": "シリーズ",
|
||||
"Enter series name": "シリーズ名を入力",
|
||||
"Series Index": "シリーズ番号",
|
||||
"Enter series index": "シリーズ番号を入力",
|
||||
"Total in Series": "シリーズ総数",
|
||||
"Enter total books in series": "シリーズの総冊数を入力",
|
||||
"Enter publisher": "出版社を入力",
|
||||
"Publication Date": "出版日",
|
||||
"Identifier": "識別子",
|
||||
"Enter book description": "本の説明を入力",
|
||||
"Change cover image": "表紙画像を変更",
|
||||
"Replace": "置き換え",
|
||||
"Unlock cover": "表紙のロックを解除",
|
||||
"Lock cover": "表紙をロック",
|
||||
"Auto-Retrieve Metadata": "メタデータを自動取得",
|
||||
"Auto-Retrieve": "自動取得",
|
||||
"Unlock all fields": "すべてのフィールドのロックを解除",
|
||||
"Unlock All": "すべてロック解除",
|
||||
"Lock all fields": "すべてのフィールドをロック",
|
||||
"Lock All": "すべてロック",
|
||||
"Reset": "リセット",
|
||||
"Edit Metadata": "メタデータを編集",
|
||||
"Locked": "ロック中",
|
||||
"Select Metadata Source": "メタデータソースを選択",
|
||||
"Keep manual input": "手動入力を保持",
|
||||
"Google Books": "Google Books",
|
||||
"Open Library": "Open Library",
|
||||
"Fiction, Science, History": "小説、科学、歴史",
|
||||
"Select Cover Image": "表紙画像を選択",
|
||||
"Open Book in New Window": "新しいウィンドウで本を開く",
|
||||
"Voices for {{lang}}": "{{lang}}の音声",
|
||||
"Yandex Translate": "Yandex翻訳",
|
||||
"YYYY or YYYY-MM-DD": "YYYYまたはYYYY-MM-DD",
|
||||
"Restore Purchase": "購入を復元",
|
||||
"No purchases found to restore.": "復元する購入が見つかりません。",
|
||||
"Failed to restore purchases.": "購入の復元に失敗しました。",
|
||||
"Failed to manage subscription.": "サブスクリプションの管理に失敗しました。",
|
||||
"Failed to load subscription plans.": "サブスクリプションプランの読み込みに失敗しました。",
|
||||
"year": "年",
|
||||
"Failed to create checkout session": "チェックアウトセッションの作成に失敗しました",
|
||||
"Storage": "ストレージ",
|
||||
"Terms of Service": "利用規約",
|
||||
"Privacy Policy": "プライバシーポリシー",
|
||||
"Disable Double Click": "ダブルクリック無効",
|
||||
"TTS not supported for this document": "TTSはこのドキュメントではサポートされていません",
|
||||
"Reset Password": "パスワードをリセット",
|
||||
"Show Reading Progress": "読書進捗を表示",
|
||||
"Reading Progress Style": "読書進捗スタイル",
|
||||
"Page Number": "ページ番号",
|
||||
"Percentage": "パーセンテージ",
|
||||
"Deleted local copy of the book: {{title}}": "書籍のローカルコピーを削除しました:{{title}}",
|
||||
"Failed to delete cloud backup of the book: {{title}}": "書籍のクラウドバックアップの削除に失敗しました:{{title}}",
|
||||
"Failed to delete local copy of the book: {{title}}": "書籍のローカルコピーの削除に失敗しました:{{title}}",
|
||||
"Are you sure to delete the local copy of the selected book?": "選択した書籍のローカルコピーを削除してもよろしいですか?",
|
||||
"Remove from Cloud & Device": "クラウドとデバイスから削除",
|
||||
"Remove from Cloud Only": "クラウドからのみ削除",
|
||||
"Remove from Device Only": "デバイスからのみ削除",
|
||||
"Disconnected": "切断されました",
|
||||
"KOReader Sync Settings": "KOReader Sync設定",
|
||||
"Sync Strategy": "同期戦略",
|
||||
"Ask on conflict": "競合時に確認",
|
||||
"Always use latest": "最新を常に使用",
|
||||
"Send changes only": "変更のみを送信",
|
||||
"Receive changes only": "変更のみを受信",
|
||||
"Disabled": "無効",
|
||||
"Checksum Method": "チェックサム方式",
|
||||
"File Content (recommended)": "ファイル内容(推奨)",
|
||||
"File Name": "ファイル名",
|
||||
"Device Name": "デバイス名",
|
||||
"Sync Tolerance": "同期許容範囲",
|
||||
"Connect to your KOReader Sync server.": "KOReader Syncサーバーに接続します。",
|
||||
"Server URL": "サーバーURL",
|
||||
"Username": "ユーザー名",
|
||||
"Your Username": "あなたのユーザー名",
|
||||
"Password": "パスワード",
|
||||
"Connect": "接続",
|
||||
"KOReader Sync": "KOReader Sync",
|
||||
"Sync Conflict": "同期競合",
|
||||
"Sync reading progress from \"{{deviceName}}\"?": "{{deviceName}}から読書進捗を同期しますか?",
|
||||
"another device": "別のデバイス",
|
||||
"Local Progress": "ローカル進捗",
|
||||
"Remote Progress": "リモート進捗",
|
||||
"Page {{page}} of {{total}} ({{percentage}}%)": "ページ {{page}} / {{total}} ({{percentage}}%)",
|
||||
"Current position": "現在の位置",
|
||||
"Approximately page {{page}} of {{total}} ({{percentage}}%)": "おおよそページ {{page}} / {{total}} ({{percentage}}%)",
|
||||
"Approximately {{percentage}}%": "おおよそ {{percentage}}%",
|
||||
"Failed to connect": "接続に失敗しました",
|
||||
"Sync Server Connected": "同期サーバーに接続されました",
|
||||
"Precision: {{precision}} digits after the decimal": "精度: {{precision}} 桁の小数点以下",
|
||||
"Sync as {{userDisplayName}}": "{{userDisplayName}} として同期"
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
"Confirm Deletion": "삭제 확인",
|
||||
"Copied to notebook": "노트북에 복사됨",
|
||||
"Copy": "복사",
|
||||
"Custom CSS": "사용자 정의 CSS",
|
||||
"Dark Mode": "다크 모드",
|
||||
"Default": "기본값",
|
||||
"Default Font": "기본 글꼴",
|
||||
@@ -28,7 +27,6 @@
|
||||
"Dictionary": "사전",
|
||||
"Download Readest": "Readest 다운로드",
|
||||
"Edit": "편집",
|
||||
"Enter your custom CSS here...": "여기에 사용자 정의 CSS를 입력하세요...",
|
||||
"Excerpts": "발췌",
|
||||
"Failed to import book(s): {{filenames}}": "책 가져오기 실패: {{filenames}}",
|
||||
"Fast": "빠름",
|
||||
@@ -48,7 +46,6 @@
|
||||
"Horizontal Direction": "수평 방향",
|
||||
"Hyphenation": "하이픈 넣기",
|
||||
"Import Books": "책 가져오기",
|
||||
"Language:": "언어:",
|
||||
"Layout": "레이아웃",
|
||||
"Light Mode": "라이트 모드",
|
||||
"Loading...": "로딩 중...",
|
||||
@@ -70,8 +67,8 @@
|
||||
"Paging Animation": "페이지 넘김 애니메이션",
|
||||
"Paragraph": "단락",
|
||||
"Parallel Read": "병렬 읽기",
|
||||
"Published:": "출판일:",
|
||||
"Publisher:": "출판사:",
|
||||
"Published": "출판일",
|
||||
"Publisher": "출판사",
|
||||
"Reading Progress Synced": "읽기 진행 상황 동기화됨",
|
||||
"Reload Page": "페이지 새로고침",
|
||||
"Reveal in File Explorer": "파일 탐색기에서 표시",
|
||||
@@ -96,16 +93,15 @@
|
||||
"Slow": "느림",
|
||||
"Solarized": "솔라라이즈드",
|
||||
"Speak": "말하기",
|
||||
"Subjects:": "주제:",
|
||||
"Subjects": "주제",
|
||||
"System Fonts": "시스템 글꼴",
|
||||
"TTS not supported in this device": "이 장치에서 TTS가 지원되지 않음",
|
||||
"Theme Color": "테마 색상",
|
||||
"Theme Mode": "테마 모드",
|
||||
"Translate": "번역",
|
||||
"Translated Text": "번역된 텍스트",
|
||||
"Unknown": "알 수 없음",
|
||||
"Untitled": "제목 없음",
|
||||
"Updated:": "업데이트일:",
|
||||
"Updated": "업데이트일",
|
||||
"Version {{version}}": "버전 {{version}}",
|
||||
"Vertical Direction": "수직 방향",
|
||||
"Welcome to your library. You can import your books here and read them anytime.": "내 서재에 오신 것을 환영합니다. 여기에서 책을 가져와 언제든지 읽을 수 있습니다.",
|
||||
@@ -114,8 +110,6 @@
|
||||
"Your Library": "내 서재",
|
||||
"TTS not supported for PDF": "PDF에서 TTS가 지원되지 않음",
|
||||
"Override Book Font": "책 글꼴 덮어쓰기",
|
||||
"Vertical Margins (px)": "수직 여백 (px)",
|
||||
"Horizontal Margins (%)": "수평 여백 (%)",
|
||||
"Apply to All Books": "모든 책에 적용",
|
||||
"Apply to This Book": "이 책에 적용",
|
||||
"Unable to fetch the translation. Try again later.": "번역을 가져올 수 없습니다. 나중에 다시 시도하세요.",
|
||||
@@ -130,8 +124,6 @@
|
||||
"Failed to download book: {{title}}": "책 다운로드 실패: {{title}}",
|
||||
"Upload Book": "책 업로드",
|
||||
"Auto Upload Books to Cloud": "책 자동으로 클라우드에 업로드",
|
||||
"{{percentage}}% of Cloud Storage Used.": "클라우드 저장소 사용량 {{percentage}}%.",
|
||||
"Storage": "저장소",
|
||||
"Book deleted: {{title}}": "책 삭제됨: {{title}}",
|
||||
"Failed to delete book: {{title}}": "책 삭제 실패: {{title}}",
|
||||
"Check Updates on Start": "시작 시 업데이트 확인",
|
||||
@@ -163,7 +155,7 @@
|
||||
"Create a Password": "비밀번호 만들기",
|
||||
"Sign up": "회원가입",
|
||||
"Signing up...": "회원가입 중...",
|
||||
"Don’t have an account? Sign up": "계정이 없습니까? 회원가입",
|
||||
"Don't have an account? Sign up": "계정이 없습니까? 회원가입",
|
||||
"Check your email for the confirmation link": "확인 링크를 위해 이메일을 확인하세요",
|
||||
"Signing in ...": "로그인 중 ...",
|
||||
"Send a magic link email": "마법의 링크 이메일 보내기",
|
||||
@@ -187,35 +179,13 @@
|
||||
"Sign in with GitHub": "GitHub로 로그인",
|
||||
"Account": "계정",
|
||||
"Failed to delete user. Please try again later.": "사용자 삭제에 실패했습니다. 나중에 다시 시도해 주세요.",
|
||||
"Unlimited Offline/Online Reading": "무제한 오프라인/온라인 읽기",
|
||||
"Unlimited Cloud Sync Devices": "무제한 클라우드 동기화 기기",
|
||||
"Essential Text-to-Speech Voices": "필수 텍스트 음성 변환 목소리",
|
||||
"DeepL Free Access": "DeepL 무료 접근",
|
||||
"Community Support": "커뮤니티 지원",
|
||||
"500 MB Cloud Sync Space": "500 MB 클라우드 동기화 공간",
|
||||
"Includes All Free Tier Benefits": "무료 등급의 모든 혜택 포함",
|
||||
"AI Summaries": "AI 요약",
|
||||
"AI Translations": "AI 번역",
|
||||
"Priority Support": "우선 지원",
|
||||
"DeepL Pro Access": "DeepL 프로 접근",
|
||||
"Expanded Text-to-Speech Voices": "확장된 텍스트 음성 변환 목소리",
|
||||
"2000 MB Cloud Sync Space": "2000 MB 클라우드 동기화 공간",
|
||||
"Includes All Plus Tier Benefits": "플러스 등급의 모든 혜택 포함",
|
||||
"Unlimited AI Summaries": "무제한 AI 요약",
|
||||
"Unlimited AI Translations": "무제한 AI 번역",
|
||||
"Unlimited DeepL Pro Access": "무제한 DeepL 프로 접근",
|
||||
"Advanced AI Tools": "고급 AI 도구",
|
||||
"Early Feature Access": "신기능 조기 접근",
|
||||
"10 GB Cloud Sync Space": "10 GB 클라우드 동기화 공간",
|
||||
"Loading profile...": "프로필 로딩 중...",
|
||||
"Features": "기능",
|
||||
"Delete Account": "계정 삭제",
|
||||
"Delete Your Account?": "계정을 삭제하시겠습니까?",
|
||||
"This action cannot be undone. All your data in the cloud will be permanently deleted.": "이 작업은 취소할 수 없습니다. 클라우드에 있는 모든 데이터가 영구적으로 삭제됩니다.",
|
||||
"Delete Permanently": "영구 삭제",
|
||||
"Free Tier": "무료 등급",
|
||||
"Plus Tier": "플러스 등급",
|
||||
"Pro Tier": "프로 등급",
|
||||
"RTL Direction": "RTL 방향",
|
||||
"Maximum Column Height": "최대 열 높이",
|
||||
"Maximum Column Width": "최대 열 너비",
|
||||
@@ -266,13 +236,13 @@
|
||||
"Clear Search": "검색 지우기",
|
||||
"Header & Footer": "헤더 및 푸터",
|
||||
"Apply also in Scrolled Mode": "스크롤 모드에서도 적용",
|
||||
"A new version of Readest is Available!": "Readest의 새 버전이 있습니다!",
|
||||
"A new version of Readest is available!": "Readest의 새 버전이 있습니다!",
|
||||
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}}가 사용 가능 (설치된 버전 {{currentVersion}}).",
|
||||
"Download and install now?": "지금 다운로드하고 설치하시겠습니까?",
|
||||
"Downloading {{downloaded}} of {{contentLength}}": "다운로드 중 {{downloaded}} / {{contentLength}}",
|
||||
"Download finished": "다운로드 완료",
|
||||
"DOWNLOAD & INSTALL": "다운로드 및 설치",
|
||||
"Changelog:": "변경 로그:",
|
||||
"Changelog": "변경 로그",
|
||||
"Software Update": "소프트웨어 업데이트",
|
||||
"Title": "제목",
|
||||
"Date Read": "읽은 날짜",
|
||||
@@ -281,10 +251,9 @@
|
||||
"Ascending": "오름차순",
|
||||
"Descending": "내림차순",
|
||||
"Sort by...": "정렬 기준...",
|
||||
"Added:": "추가됨:",
|
||||
"Format:": "형식:",
|
||||
"Added": "추가됨",
|
||||
"GuanKiapTsingKhai-T": "GuanKiapTsingKhai-T",
|
||||
"Description:": "설명:",
|
||||
"Description": "설명",
|
||||
"No description available": "설명 없음",
|
||||
"List": "목록",
|
||||
"Grid": "격자",
|
||||
@@ -301,7 +270,7 @@
|
||||
"Checking for updates...": "업데이트 확인 중...",
|
||||
"Error checking for updates": "업데이트 확인 중 오류 발생",
|
||||
"Details": "세부정보",
|
||||
"File Size:": "파일 크기:",
|
||||
"File Size": "파일 크기",
|
||||
"Auto Detect": "자동 감지",
|
||||
"Next Section": "다음 섹션",
|
||||
"Previous Section": "이전 섹션",
|
||||
@@ -340,14 +309,12 @@
|
||||
"Synced at {{time}}": "{{time}}에 동기화됨",
|
||||
"Never synced": "동기화된 적 없음",
|
||||
"Show Remaining Time": "남은 시간 표시",
|
||||
"Show Page Number": "페이지 번호 표시",
|
||||
"{{time}} min left in chapter": "{{time}}분 남음",
|
||||
"Override Book Color": "책 색상 덮어쓰기",
|
||||
"Login Required": "로그인 필요",
|
||||
"Quota Exceeded": "한도 초과",
|
||||
"{{percentage}}% of Daily Translation Characters Used.": "일일 번역 문자 사용량 {{percentage}}%.",
|
||||
"Translation Characters": "번역 문자",
|
||||
"Daily translation quota reached. Select another translate service to proceed.": "일일 번역 할당량에 도달했습니다. 계속 진행하려면 다른 번역 서비스를 선택하세요.",
|
||||
"{{engine}}: {{count}} voices_other": "{{engine}}: {{count}}개의 음성",
|
||||
"Something went wrong. Don't worry, our team has been notified and we're working on a fix.": "문제가 발생했습니다. 걱정하지 마세요, 저희 팀이 이미 알림을 받았으며 해결 작업을 진행 중입니다.",
|
||||
"Error Details:": "오류 세부사항:",
|
||||
@@ -356,5 +323,182 @@
|
||||
"Contact Support": "지원팀에 문의",
|
||||
"Code Highlighting": "코드 하이라이팅",
|
||||
"Enable Highlighting": "하이라이팅 활성화",
|
||||
"Code Language": "코드 언어"
|
||||
"Code Language": "코드 언어",
|
||||
"Top Margin (px)": "위쪽 여백 (px)",
|
||||
"Bottom Margin (px)": "아래쪽 여백 (px)",
|
||||
"Right Margin (px)": "오른쪽 여백 (px)",
|
||||
"Left Margin (px)": "왼쪽 여백 (px)",
|
||||
"Column Gap (%)": "열 간격 (%)",
|
||||
"Always Show Status Bar": "상태 표시줄 항상 표시",
|
||||
"Translation Not Available": "번역을 사용할 수 없음",
|
||||
"Custom Content CSS": "콘텐츠 CSS",
|
||||
"Enter CSS for book content styling...": "도서 콘텐츠 스타일을 위한 CSS 입력...",
|
||||
"Custom Reader UI CSS": "리더 UI CSS",
|
||||
"Enter CSS for reader interface styling...": "리더 인터페이스 스타일용 CSS 입력...",
|
||||
"Crop": "자르기",
|
||||
"Book Covers": "책 표지",
|
||||
"Fit": "맞춤",
|
||||
"Reset {{settings}}": "{{settings}} 재설정",
|
||||
"Reset Settings": "설정 재설정",
|
||||
"{{count}} pages left in chapter_other": "남은 페이지 {{count}}장",
|
||||
"Show Remaining Pages": "남은 페이지 보기",
|
||||
"Source Han Serif CN VF": "Source Han Serif",
|
||||
"Huiwen-mincho": "Huiwen Mincho",
|
||||
"KingHwa_OldSong": "KingHwa Song",
|
||||
"Manage Subscription": "구독 관리",
|
||||
"Coming Soon": "곧 출시 예정",
|
||||
"Upgrade to {{plan}}": "{{plan}}로 업그레이드",
|
||||
"Upgrade to Plus or Pro": "Plus 또는 Pro로 업그레이드",
|
||||
"Current Plan": "현재 요금제",
|
||||
"Plan Limits": "요금제 한도",
|
||||
"Processing your payment...": "결제를 처리 중입니다...",
|
||||
"Please wait while we confirm your subscription.": "구독을 확인하는 중입니다. 잠시만 기다려주세요.",
|
||||
"Payment Processing": "결제 처리 중",
|
||||
"Your payment is being processed. This usually takes a few moments.": "결제가 처리되고 있습니다. 잠시만 기다려주세요.",
|
||||
"Payment Failed": "결제 실패",
|
||||
"We couldn't process your subscription. Please try again or contact support if the issue persists.": "구독을 처리할 수 없습니다. 다시 시도하거나 문제가 계속되면 지원팀에 문의해주세요.",
|
||||
"Back to Profile": "프로필로 돌아가기",
|
||||
"Subscription Successful!": "구독이 완료되었습니다!",
|
||||
"Thank you for your subscription! Your payment has been processed successfully.": "구독해주셔서 감사합니다! 결제가 성공적으로 처리되었습니다.",
|
||||
"Email:": "이메일:",
|
||||
"Plan:": "요금제:",
|
||||
"Amount:": "금액:",
|
||||
"Subscription ID:": "구독 ID:",
|
||||
"Go to Library": "라이브러리로 이동",
|
||||
"Need help? Contact our support team at support@readest.com": "도움이 필요하시면 support@readest.com으로 문의해주세요.",
|
||||
"Free Plan": "무료 요금제",
|
||||
"month": "월",
|
||||
"AI Translations (per day)": "AI 번역 (일 기준)",
|
||||
"Plus Plan": "Plus 요금제",
|
||||
"Includes All Free Plan Benefits": "무료 요금제의 모든 혜택 포함",
|
||||
"Pro Plan": "Pro 요금제",
|
||||
"More AI Translations": "더 많은 AI 번역",
|
||||
"Complete Your Subscription": "구독 완료",
|
||||
"{{percentage}}% of Cloud Sync Space Used.": "{{percentage}}%의 클라우드 동기화 공간 사용됨.",
|
||||
"Cloud Sync Storage": "클라우드 저장 공간",
|
||||
"Disable": "비활성화",
|
||||
"Enable": "활성화",
|
||||
"Upgrade to Readest Premium": "Readest Premium으로 업그레이드",
|
||||
"Show Source Text": "원본 텍스트 표시",
|
||||
"Cross-Platform Sync": "크로스 플랫폼 동기화",
|
||||
"Seamlessly sync your library, progress, highlights, and notes across all your devices—never lose your place again.": "라이브러리와 진행 상황을 모든 기기에서 동기화합니다.",
|
||||
"Customizable Reading": "맞춤형 읽기",
|
||||
"Personalize every detail with adjustable fonts, layouts, themes, and advanced display settings for the perfect reading experience.": "폰트, 레이아웃, 테마를 자유롭게 조정하세요.",
|
||||
"AI Read Aloud": "AI 음성 읽기",
|
||||
"Enjoy hands-free reading with natural-sounding AI voices that bring your books to life.": "자연스러운 AI 음성으로 책을 들어보세요.",
|
||||
"AI Translations": "AI 번역",
|
||||
"Translate any text instantly with the power of Google, Azure, or DeepL—understand content in any language.": "Google, Azure, DeepL로 즉시 번역합니다.",
|
||||
"Connect with fellow readers and get help fast in our friendly community channels.": "커뮤니티에서 도움과 소통을 즐기세요.",
|
||||
"Unlimited AI Read Aloud Hours": "무제한 AI 음성 읽기",
|
||||
"Listen without limits—convert as much text as you like into immersive audio.": "제한 없이 텍스트를 음성으로 변환하세요.",
|
||||
"Unlock enhanced translation capabilities with more daily usage and advanced options.": "고급 번역 기능을 더 많이 이용하세요.",
|
||||
"DeepL Pro Access": "DeepL Pro 이용",
|
||||
"Enjoy faster responses and dedicated assistance whenever you need help.": "우선 지원을 이용하세요.",
|
||||
"Daily translation quota reached. Upgrade your plan to continue using AI translations.": "일일 번역 한도에 도달했습니다. 업그레이드하여 계속 사용하세요.",
|
||||
"Includes All Plus Plan Benefits": "모든 플러스 플랜 혜택 포함",
|
||||
"Early Feature Access": "신기능 우선 액세스",
|
||||
"Be the first to explore new features, updates, and innovations before anyone else.": "새로운 기능, 업데이트, 혁신을 누구보다 먼저 체험하세요.",
|
||||
"Advanced AI Tools": "고급 AI 도구",
|
||||
"Harness powerful AI tools for smarter reading, translation, and content discovery.": "스마트한 독서, 번역, 콘텐츠 발견을 위한 강력한 AI 도구를 활용하세요.",
|
||||
"Translate up to 100,000 characters daily with the most accurate translation engine available.": "가장 정확한 번역 엔진으로 매일 최대 10만 자를 번역하세요.",
|
||||
"Translate up to 500,000 characters daily with the most accurate translation engine available.": "가장 정확한 번역 엔진으로 매일 최대 50만 자를 번역하세요.",
|
||||
"Securely store and access your entire reading collection with up to 5 GB of cloud storage.": "최대 5GB의 안전한 클라우드 저장소로 전체 독서 컬렉션을 저장하고 액세스하세요.",
|
||||
"Securely store and access your entire reading collection with up to 20 GB of cloud storage.": "최대 20GB의 안전한 클라우드 저장소로 전체 독서 컬렉션을 저장하고 액세스하세요.",
|
||||
"Deleted cloud backup of the book: {{title}}": "책의 클라우드 백업이 삭제됨: {{title}}",
|
||||
"Are you sure to delete the cloud backup of the selected book?": "선택한 책의 클라우드 백업을 삭제하시겠습니까?",
|
||||
"What's New in Readest": "Readest의 새로운 기능",
|
||||
"Enter book title": "도서 제목 입력",
|
||||
"Subtitle": "부제목",
|
||||
"Enter book subtitle": "도서 부제목 입력",
|
||||
"Enter author name": "저자명 입력",
|
||||
"Series": "시리즈",
|
||||
"Enter series name": "시리즈명 입력",
|
||||
"Series Index": "시리즈 번호",
|
||||
"Enter series index": "시리즈 번호 입력",
|
||||
"Total in Series": "시리즈 총권수",
|
||||
"Enter total books in series": "시리즈 총 도서 수 입력",
|
||||
"Enter publisher": "출판사 입력",
|
||||
"Publication Date": "출간일",
|
||||
"Identifier": "식별자",
|
||||
"Enter book description": "도서 설명 입력",
|
||||
"Change cover image": "표지 이미지 변경",
|
||||
"Replace": "교체",
|
||||
"Unlock cover": "표지 잠금 해제",
|
||||
"Lock cover": "표지 잠금",
|
||||
"Auto-Retrieve Metadata": "메타데이터 자동 검색",
|
||||
"Auto-Retrieve": "자동 검색",
|
||||
"Unlock all fields": "모든 필드 잠금 해제",
|
||||
"Unlock All": "모두 잠금 해제",
|
||||
"Lock all fields": "모든 필드 잠금",
|
||||
"Lock All": "모두 잠금",
|
||||
"Reset": "초기화",
|
||||
"Edit Metadata": "메타데이터 편집",
|
||||
"Locked": "잠김",
|
||||
"Select Metadata Source": "메타데이터 소스 선택",
|
||||
"Keep manual input": "수동 입력 유지",
|
||||
"Google Books": "구글 북스",
|
||||
"Open Library": "오픈 라이브러리",
|
||||
"Fiction, Science, History": "소설, 과학, 역사",
|
||||
"Select Cover Image": "표지 이미지 선택",
|
||||
"Open Book in New Window": "새 창에서 책 열기",
|
||||
"Voices for {{lang}}": "{{lang}}의 음성",
|
||||
"Yandex Translate": "Yandex 번역",
|
||||
"YYYY or YYYY-MM-DD": "YYYY 또는 YYYY-MM-DD",
|
||||
"Restore Purchase": "구매 복원",
|
||||
"No purchases found to restore.": "복원할 구매가 없습니다.",
|
||||
"Failed to restore purchases.": "구매 복원에 실패했습니다.",
|
||||
"Failed to manage subscription.": "구독 관리에 실패했습니다.",
|
||||
"Failed to load subscription plans.": "구독 플랜 로드에 실패했습니다.",
|
||||
"year": "년",
|
||||
"Failed to create checkout session": "체크아웃 세션 생성 실패",
|
||||
"Storage": "저장소",
|
||||
"Terms of Service": "서비스 약관",
|
||||
"Privacy Policy": "개인정보 보호정책",
|
||||
"Disable Double Click": "더블 클릭 비활성화",
|
||||
"TTS not supported for this document": "TTS가 이 문서에서 지원되지 않습니다",
|
||||
"Reset Password": "비밀번호 재설정",
|
||||
"Show Reading Progress": "읽기 진행 상황 표시",
|
||||
"Reading Progress Style": "읽기 진행 상황 스타일",
|
||||
"Page Number": "페이지 번호",
|
||||
"Percentage": "백분율",
|
||||
"Deleted local copy of the book: {{title}}": "책의 로컬 사본이 삭제됨: {{title}}",
|
||||
"Failed to delete cloud backup of the book: {{title}}": "책의 클라우드 백업 삭제에 실패했습니다: {{title}}",
|
||||
"Failed to delete local copy of the book: {{title}}": "책의 로컬 사본 삭제에 실패했습니다: {{title}}",
|
||||
"Are you sure to delete the local copy of the selected book?": "선택한 책의 로컬 사본을 삭제하시겠습니까?",
|
||||
"Remove from Cloud & Device": "클라우드 및 장치에서 제거",
|
||||
"Remove from Cloud Only": "클라우드에서만 제거",
|
||||
"Remove from Device Only": "장치에서만 제거",
|
||||
"Disconnected": "연결 끊김",
|
||||
"KOReader Sync Settings": "KOReader Sync 설정",
|
||||
"Sync Strategy": "동기화 전략",
|
||||
"Ask on conflict": "충돌 시 묻기",
|
||||
"Always use latest": "항상 최신 버전 사용",
|
||||
"Send changes only": "변경 사항만 전송",
|
||||
"Receive changes only": "변경 사항만 수신",
|
||||
"Disabled": "비활성화",
|
||||
"Checksum Method": "체크섬 방법",
|
||||
"File Content (recommended)": "파일 내용 (권장)",
|
||||
"File Name": "파일 이름",
|
||||
"Device Name": "장치 이름",
|
||||
"Sync Tolerance": "동기화 허용 오차",
|
||||
"Connect to your KOReader Sync server.": "KOReader Sync 서버에 연결합니다.",
|
||||
"Server URL": "서버 URL",
|
||||
"Username": "사용자 이름",
|
||||
"Your Username": "당신의 사용자 이름",
|
||||
"Password": "비밀번호",
|
||||
"Connect": "연결",
|
||||
"KOReader Sync": "KOReader Sync",
|
||||
"Sync Conflict": "동기화 충돌",
|
||||
"Sync reading progress from \"{{deviceName}}\"?": " \"{{deviceName}}\"에서 읽기 진행 상황을 동기화하시겠습니까?",
|
||||
"another device": "다른 장치",
|
||||
"Local Progress": "로컬 진행 상황",
|
||||
"Remote Progress": "원격 진행 상황",
|
||||
"Page {{page}} of {{total}} ({{percentage}}%)": "페이지 {{page}} / {{total}} ({{percentage}}%)",
|
||||
"Current position": "현재 위치",
|
||||
"Approximately page {{page}} of {{total}} ({{percentage}}%)": "대략 페이지 {{page}} / {{total}} ({{percentage}}%)",
|
||||
"Approximately {{percentage}}%": "대략 {{percentage}}%",
|
||||
"Failed to connect": "연결 실패",
|
||||
"Sync Server Connected": "동기화 서버에 연결됨",
|
||||
"Precision: {{precision}} digits after the decimal": "정밀도: {{precision}} 자리의 소수점 이하",
|
||||
"Sync as {{userDisplayName}}": "{{userDisplayName}}로 동기화"
|
||||
}
|
||||
|
||||
@@ -57,8 +57,6 @@
|
||||
"Import Books": "Boeken importeren",
|
||||
"Select Multiple Books": "Meerdere boeken selecteren",
|
||||
"Select Books": "Boeken selecteren",
|
||||
"Storage": "Opslag",
|
||||
"{{percentage}}% of Cloud Storage Used.": "{{percentage}}% van cloudopslag gebruikt.",
|
||||
"Logged in as {{userDisplayName}}": "Ingelogd als {{userDisplayName}}",
|
||||
"Logged in": "Ingelogd",
|
||||
"Account": "Account",
|
||||
@@ -169,8 +167,6 @@
|
||||
"Hyphenation": "Woordafbreking",
|
||||
"Override Book Layout": "Boekopmaak overschrijven",
|
||||
"Page": "Pagina",
|
||||
"Vertical Margins (px)": "Verticale marges (px)",
|
||||
"Horizontal Margins (%)": "Horizontale marges (%)",
|
||||
"Maximum Number of Columns": "Maximum aantal kolommen",
|
||||
"Maximum Column Height": "Maximale kolomhoogte",
|
||||
"Maximum Column Width": "Maximale kolombreedte",
|
||||
@@ -191,8 +187,6 @@
|
||||
"Volume Keys for Page Flip": "Volumetoetsen voor pagina omslaan",
|
||||
"Clicks for Page Flip": "Klikken voor pagina omslaan",
|
||||
"Swap Clicks Area": "Klikgebieden omwisselen",
|
||||
"Custom CSS": "Aangepaste CSS",
|
||||
"Enter your custom CSS here...": "Voer hier uw aangepaste CSS in...",
|
||||
"Apply": "Toepassen",
|
||||
"Font": "Lettertype",
|
||||
"Layout": "Opmaak",
|
||||
@@ -219,7 +213,6 @@
|
||||
"TOC": "Inhoudsopgave",
|
||||
"Sidebar": "Zijbalk",
|
||||
"TTS not supported for PDF": "TTS niet ondersteund voor PDF",
|
||||
"TTS not supported in this device": "TTS niet ondersteund op dit apparaat",
|
||||
"No Timeout": "Geen time-out",
|
||||
"{{value}} minute": "{{value}} minuut",
|
||||
"{{value}} minutes": "{{value}} minuten",
|
||||
@@ -229,31 +222,9 @@
|
||||
"Fast": "Snel",
|
||||
"Reading Progress Synced": "Leesvoortgang gesynchroniseerd",
|
||||
"Failed to delete user. Please try again later.": "Gebruiker verwijderen mislukt. Probeer het later opnieuw.",
|
||||
"Free Tier": "Gratis niveau",
|
||||
"Community Support": "Community-ondersteuning",
|
||||
"DeepL Free Access": "DeepL gratis toegang",
|
||||
"Essential Text-to-Speech Voices": "Essentiële tekst-naar-spraak stemmen",
|
||||
"Unlimited Offline/Online Reading": "Onbeperkt offline/online lezen",
|
||||
"Unlimited Cloud Sync Devices": "Onbeperkt aantal cloud-synchronisatie apparaten",
|
||||
"500 MB Cloud Sync Space": "500 MB cloud-synchronisatieruimte",
|
||||
"Plus Tier": "Plus niveau",
|
||||
"Includes All Free Tier Benefits": "Inclusief alle voordelen van het gratis niveau",
|
||||
"Priority Support": "Prioriteitsondersteuning",
|
||||
"AI Summaries": "AI-samenvattingen",
|
||||
"AI Translations": "AI-vertalingen",
|
||||
"DeepL Pro Access": "DeepL Pro toegang",
|
||||
"Expanded Text-to-Speech Voices": "Uitgebreide tekst-naar-spraak stemmen",
|
||||
"2000 MB Cloud Sync Space": "2000 MB cloud-synchronisatieruimte",
|
||||
"Pro Tier": "Pro niveau",
|
||||
"Includes All Plus Tier Benefits": "Inclusief alle voordelen van het Plus niveau",
|
||||
"Unlimited AI Summaries": "Onbeperkte AI-samenvattingen",
|
||||
"Unlimited AI Translations": "Onbeperkte AI-vertalingen",
|
||||
"Unlimited DeepL Pro Access": "Onbeperkte DeepL Pro toegang",
|
||||
"Advanced AI Tools": "Geavanceerde AI-tools",
|
||||
"Early Feature Access": "Vroege toegang tot functies",
|
||||
"10 GB Cloud Sync Space": "10 GB cloud-synchronisatieruimte",
|
||||
"Loading profile...": "Profiel laden...",
|
||||
"Features": "Functies",
|
||||
"Sign Out": "Uitloggen",
|
||||
"Delete Account": "Account verwijderen",
|
||||
"Delete Your Account?": "Uw account verwijderen?",
|
||||
@@ -266,24 +237,22 @@
|
||||
"Error checking for updates": "Fout bij controleren op updates",
|
||||
"Book Details": "Boekdetails",
|
||||
"Unknown": "Onbekend",
|
||||
"Publisher:": "Uitgever:",
|
||||
"Published:": "Gepubliceerd:",
|
||||
"Updated:": "Bijgewerkt:",
|
||||
"Added:": "Toegevoegd:",
|
||||
"Language:": "Taal:",
|
||||
"Subjects:": "Onderwerpen:",
|
||||
"Format:": "Formaat:",
|
||||
"File Size:": "Bestandsgrootte:",
|
||||
"Description:": "Beschrijving:",
|
||||
"Publisher": "Uitgever",
|
||||
"Published": "Gepubliceerd",
|
||||
"Updated": "Bijgewerkt",
|
||||
"Added": "Toegevoegd",
|
||||
"Subjects": "Onderwerpen",
|
||||
"File Size": "Bestandsgrootte",
|
||||
"Description": "Beschrijving",
|
||||
"No description available": "Geen beschrijving beschikbaar",
|
||||
"Drop to Import Books": "Sleep om boeken te importeren",
|
||||
"A new version of Readest is Available!": "Een nieuwe versie van Readest is beschikbaar!",
|
||||
"A new version of Readest is available!": "Een nieuwe versie van Readest is beschikbaar!",
|
||||
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} is beschikbaar (geïnstalleerde versie {{currentVersion}}).",
|
||||
"Download and install now?": "Nu downloaden en installeren?",
|
||||
"Downloading {{downloaded}} of {{contentLength}}": "Downloaden {{downloaded}} van {{contentLength}}",
|
||||
"Download finished": "Download voltooid",
|
||||
"DOWNLOAD & INSTALL": "DOWNLOADEN & INSTALLEREN",
|
||||
"Changelog:": "Wijzigingslogboek:",
|
||||
"Changelog": "Wijzigingslogboek",
|
||||
"Software Update": "Software-update",
|
||||
"LXGW WenKai GB Screen": "LXGW WenKai GB Scherm",
|
||||
"LXGW WenKai TC": "LXGW WenKai TC",
|
||||
@@ -301,7 +270,7 @@
|
||||
"Reveal in Finder": "Tonen in Finder",
|
||||
"Reveal in File Explorer": "Tonen in Verkenner",
|
||||
"Reveal in Folder": "Tonen in map",
|
||||
"Don’t have an account? Sign up": "Nog geen account? Registreer u",
|
||||
"Don't have an account? Sign up": "Nog geen account? Registreer u",
|
||||
"All the world's a stage,\nAnd all the men and women merely players;\nThey have their exits and their entrances,\nAnd one man in his time plays many parts,\nHis acts being seven ages.\n\n— William Shakespeare": "De hele wereld is een toneel,\nEn alle mannen en vrouwen slechts spelers;\nZij hebben hun uitgangen en hun entrees,\nEn één man speelt in zijn tijd vele rollen,\nZijn daden zijnde zeven leeftijden.\n\n— William Shakespeare",
|
||||
"Next Section": "Volgende sectie",
|
||||
"Previous Section": "Vorige sectie",
|
||||
@@ -333,7 +302,7 @@
|
||||
"Security": "Beveiliging",
|
||||
"Allow JavaScript": "JavaScript toestaan",
|
||||
"Enable only if you trust the file.": "Schakel alleen in als u het bestand vertrouwt.",
|
||||
"Sort TOC by Page": "__STRING_NOT_TRANSLATED__",
|
||||
"Sort TOC by Page": "TOC sorteren op pagina",
|
||||
"Search in {{count}} Book(s)..._one": "Zoeken in {{count}} Boek...",
|
||||
"Search in {{count}} Book(s)..._other": "Zoeken in {{count}} Boeken...",
|
||||
"No notes match your search": "Geen notities gevonden",
|
||||
@@ -342,14 +311,12 @@
|
||||
"Synced at {{time}}": "Gesynchroniseerd om {{time}}",
|
||||
"Never synced": "Nooit gesynchroniseerd",
|
||||
"Show Remaining Time": "Toon resterende tijd",
|
||||
"Show Page Number": "Toon paginanummer",
|
||||
"{{time}} min left in chapter": "Nog {{time}} min in hoofdstuk",
|
||||
"Override Book Color": "Boekkleur overschrijven",
|
||||
"Login Required": "Inloggen vereist",
|
||||
"Quota Exceeded": "Limiet overschreden",
|
||||
"{{percentage}}% of Daily Translation Characters Used.": "{{percentage}}% van de dagelijkse vertaaltekens gebruikt.",
|
||||
"Translation Characters": "Vertaaltekens",
|
||||
"Daily translation quota reached. Select another translate service to proceed.": "Dagelijkse vertaalquotum bereikt. Selecteer een andere vertaalservice om door te gaan.",
|
||||
"{{engine}}: {{count}} voices_one": "{{engine}}: {{count}} stem",
|
||||
"{{engine}}: {{count}} voices_other": "{{engine}}: {{count}} stemmen",
|
||||
"Something went wrong. Don't worry, our team has been notified and we're working on a fix.": "Er is iets misgegaan. Maak je geen zorgen, ons team is op de hoogte gebracht en we werken aan een oplossing.",
|
||||
@@ -359,5 +326,183 @@
|
||||
"Contact Support": "Neem contact op met ondersteuning",
|
||||
"Code Highlighting": "Code markering",
|
||||
"Enable Highlighting": "Markering inschakelen",
|
||||
"Code Language": "Code taal"
|
||||
"Code Language": "Code taal",
|
||||
"Top Margin (px)": "Bovenmarge (px)",
|
||||
"Bottom Margin (px)": "Ondermarge (px)",
|
||||
"Right Margin (px)": "Rechter marge (px)",
|
||||
"Left Margin (px)": "Linker marge (px)",
|
||||
"Column Gap (%)": "Kolomafstand (%)",
|
||||
"Always Show Status Bar": "Statusbalk altijd weergeven",
|
||||
"Translation Not Available": "Vertaling niet beschikbaar",
|
||||
"Custom Content CSS": "Aangepaste inhoud-CSS",
|
||||
"Enter CSS for book content styling...": "Voer CSS in voor de opmaak van de boekinhoud...",
|
||||
"Custom Reader UI CSS": "Aangepaste interface-CSS",
|
||||
"Enter CSS for reader interface styling...": "Voer CSS in voor de opmaak van de leesinterface...",
|
||||
"Crop": "Bijsnijden",
|
||||
"Book Covers": "Boekomslagen",
|
||||
"Fit": "Passend maken",
|
||||
"Reset {{settings}}": "Reset {{settings}}",
|
||||
"Reset Settings": "Instellingen resetten",
|
||||
"{{count}} pages left in chapter_one": "Nog {{count}} pagina in hoofdstuk",
|
||||
"{{count}} pages left in chapter_other": "Nog {{count}} pagina’s in hoofdstuk",
|
||||
"Show Remaining Pages": "Toon resterende pagina’s",
|
||||
"Source Han Serif CN VF": "Source Han Serif",
|
||||
"Huiwen-mincho": "Huiwen Mincho",
|
||||
"KingHwa_OldSong": "KingHwa Song",
|
||||
"Manage Subscription": "Abonnement beheren",
|
||||
"Coming Soon": "Binnenkort beschikbaar",
|
||||
"Upgrade to {{plan}}": "Upgrade naar {{plan}}",
|
||||
"Upgrade to Plus or Pro": "Upgrade naar Plus of Pro",
|
||||
"Current Plan": "Huidig abonnement",
|
||||
"Plan Limits": "Abonnementslimieten",
|
||||
"Processing your payment...": "Je betaling wordt verwerkt...",
|
||||
"Please wait while we confirm your subscription.": "Even geduld terwijl we je abonnement bevestigen.",
|
||||
"Payment Processing": "Betaling verwerken",
|
||||
"Your payment is being processed. This usually takes a few moments.": "Je betaling wordt verwerkt. Dit duurt meestal een paar seconden.",
|
||||
"Payment Failed": "Betaling mislukt",
|
||||
"Back to Profile": "Terug naar profiel",
|
||||
"Subscription Successful!": "Abonnement succesvol!",
|
||||
"Email:": "E-mail:",
|
||||
"Plan:": "Abonnement:",
|
||||
"Amount:": "Bedrag:",
|
||||
"Subscription ID:": "Abonnements-ID:",
|
||||
"Go to Library": "Ga naar bibliotheek",
|
||||
"Need help? Contact our support team at support@readest.com": "Hulp nodig? Neem contact op met support via support@readest.com",
|
||||
"Free Plan": "Gratis abonnement",
|
||||
"month": "maand",
|
||||
"AI Translations (per day)": "AI-vertalingen (per dag)",
|
||||
"Plus Plan": "Plus-abonnement",
|
||||
"Includes All Free Plan Benefits": "Bevat alle voordelen van het gratis abonnement",
|
||||
"Pro Plan": "Pro-abonnement",
|
||||
"More AI Translations": "Meer AI-vertalingen",
|
||||
"We couldn't process your subscription. Please try again or contact support if the issue persists.": "We konden je abonnement niet verwerken. Probeer het opnieuw of neem contact op met support als het probleem blijft bestaan.",
|
||||
"Thank you for your subscription! Your payment has been processed successfully.": "Bedankt voor je abonnement! Je betaling is succesvol verwerkt.",
|
||||
"Complete Your Subscription": "Voltooi uw abonnement",
|
||||
"{{percentage}}% of Cloud Sync Space Used.": "{{percentage}}% van de cloud-synchronisatie ruimte gebruikt.",
|
||||
"Cloud Sync Storage": "Cloud Sync Opslag",
|
||||
"Disable": "Uitschakelen",
|
||||
"Enable": "Inschakelen",
|
||||
"Upgrade to Readest Premium": "Upgrade naar Readest Premium",
|
||||
"Show Source Text": "Toon brontekst",
|
||||
"Cross-Platform Sync": "Cross-Platform Synchronisatie",
|
||||
"Seamlessly sync your library, progress, highlights, and notes across all your devices—never lose your place again.": "Synchroniseer naadloos je bibliotheek, voortgang, markeringen en notities op al je apparaten—verlies nooit meer je plek.",
|
||||
"Customizable Reading": "Aanpasbaar Lezen",
|
||||
"Personalize every detail with adjustable fonts, layouts, themes, and advanced display settings for the perfect reading experience.": "Personaliseer elk detail met aanpasbare lettertypen, lay-outs, thema's en geavanceerde weergave-instellingen voor de perfecte leeservaring.",
|
||||
"AI Read Aloud": "AI Voorlezen",
|
||||
"Enjoy hands-free reading with natural-sounding AI voices that bring your books to life.": "Geniet van handsfree lezen met natuurlijk klinkende AI-stemmen die je boeken tot leven brengen.",
|
||||
"AI Translations": "AI Vertalingen",
|
||||
"Translate any text instantly with the power of Google, Azure, or DeepL—understand content in any language.": "Vertaal elke tekst direct met de kracht van Google, Azure of DeepL—begrijp inhoud in elke taal.",
|
||||
"Connect with fellow readers and get help fast in our friendly community channels.": "Maak contact met medelezers en krijg snel hulp in onze vriendelijke community kanalen.",
|
||||
"Unlimited AI Read Aloud Hours": "Onbeperkte AI Voorlees Uren",
|
||||
"Listen without limits—convert as much text as you like into immersive audio.": "Luister zonder limieten—zet zoveel tekst als je wilt om in meeslepende audio.",
|
||||
"Unlock enhanced translation capabilities with more daily usage and advanced options.": "Ontsluit verbeterde vertaalfuncties met meer dagelijks gebruik en geavanceerde opties.",
|
||||
"DeepL Pro Access": "DeepL Pro Toegang",
|
||||
"Enjoy faster responses and dedicated assistance whenever you need help.": "Geniet van snellere reacties en toegewijde hulp wanneer je hulp nodig hebt.",
|
||||
"Daily translation quota reached. Upgrade your plan to continue using AI translations.": "Dagelijks vertaalquotum bereikt. Upgrade je abonnement om AI vertalingen te blijven gebruiken.",
|
||||
"Includes All Plus Plan Benefits": "Inclusief Alle Plus Plan Voordelen",
|
||||
"Early Feature Access": "Vroege Toegang tot Functies",
|
||||
"Be the first to explore new features, updates, and innovations before anyone else.": "Ontdek als eerste nieuwe functies, updates en innovaties.",
|
||||
"Advanced AI Tools": "Geavanceerde AI Tools",
|
||||
"Harness powerful AI tools for smarter reading, translation, and content discovery.": "Benut krachtige AI tools voor slimmer lezen, vertalen en content ontdekking.",
|
||||
"Translate up to 100,000 characters daily with the most accurate translation engine available.": "Vertaal dagelijks tot 100.000 tekens met de meest nauwkeurige vertaalengine.",
|
||||
"Translate up to 500,000 characters daily with the most accurate translation engine available.": "Vertaal dagelijks tot 500.000 tekens met de meest nauwkeurige vertaalengine.",
|
||||
"Securely store and access your entire reading collection with up to 5 GB of cloud storage.": "Sla je hele leesverzameling veilig op en benader deze met tot 5 GB cloudopslag.",
|
||||
"Securely store and access your entire reading collection with up to 20 GB of cloud storage.": "Sla je hele leesverzameling veilig op en benader deze met tot 20 GB cloudopslag.",
|
||||
"Deleted cloud backup of the book: {{title}}": "Verwijderde cloudback-up van het boek: {{title}}",
|
||||
"Are you sure to delete the cloud backup of the selected book?": "Weet je zeker dat je de cloudback-up van het geselecteerde boek wilt verwijderen?",
|
||||
"What's New in Readest": "Wat is er nieuw in Readest",
|
||||
"Enter book title": "Voer boektitel in",
|
||||
"Subtitle": "Ondertitel",
|
||||
"Enter book subtitle": "Voer boek ondertitel in",
|
||||
"Enter author name": "Voer auteursnaam in",
|
||||
"Series": "Serie",
|
||||
"Enter series name": "Voer serienaam in",
|
||||
"Series Index": "Serie nummer",
|
||||
"Enter series index": "Voer serie nummer in",
|
||||
"Total in Series": "Totaal in serie",
|
||||
"Enter total books in series": "Voer totaal aantal boeken in serie in",
|
||||
"Enter publisher": "Voer uitgever in",
|
||||
"Publication Date": "Publicatiedatum",
|
||||
"Identifier": "Identificatie",
|
||||
"Enter book description": "Voer boekbeschrijving in",
|
||||
"Change cover image": "Wijzig omslagafbeelding",
|
||||
"Replace": "Vervangen",
|
||||
"Unlock cover": "Omslag ontgrendelen",
|
||||
"Lock cover": "Omslag vergrendelen",
|
||||
"Auto-Retrieve Metadata": "Metadata automatisch ophalen",
|
||||
"Auto-Retrieve": "Automatisch ophalen",
|
||||
"Unlock all fields": "Alle velden ontgrendelen",
|
||||
"Unlock All": "Alles ontgrendelen",
|
||||
"Lock all fields": "Alle velden vergrendelen",
|
||||
"Lock All": "Alles vergrendelen",
|
||||
"Reset": "Herstellen",
|
||||
"Edit Metadata": "Metadata bewerken",
|
||||
"Locked": "Vergrendeld",
|
||||
"Select Metadata Source": "Selecteer metadata bron",
|
||||
"Keep manual input": "Handmatige invoer behouden",
|
||||
"Google Books": "Google Books",
|
||||
"Open Library": "Open Library",
|
||||
"Fiction, Science, History": "Fictie, Wetenschap, Geschiedenis",
|
||||
"Select Cover Image": "Selecteer omslagafbeelding",
|
||||
"Open Book in New Window": "Open boek in nieuw venster",
|
||||
"Voices for {{lang}}": "Stemmen voor {{lang}}",
|
||||
"Yandex Translate": "Yandex Vertalen",
|
||||
"YYYY or YYYY-MM-DD": "YYYY of YYYY-MM-DD",
|
||||
"Restore Purchase": "Herstel aankoop",
|
||||
"No purchases found to restore.": "Geen aankopen gevonden om te herstellen.",
|
||||
"Failed to restore purchases.": "Herstellen van aankopen is mislukt.",
|
||||
"Failed to manage subscription.": "Beheren van abonnement is mislukt.",
|
||||
"Failed to load subscription plans.": "Laden van abonnement plannen is mislukt.",
|
||||
"year": "jaar",
|
||||
"Failed to create checkout session": "Kon de afreken sessie niet aanmaken",
|
||||
"Storage": "Opslag",
|
||||
"Terms of Service": "Gebruiksvoorwaarden",
|
||||
"Privacy Policy": "Privacybeleid",
|
||||
"Disable Double Click": "Dubbelklikken uitschakelen",
|
||||
"TTS not supported for this document": "TTS niet ondersteund voor dit document",
|
||||
"Reset Password": "Wachtwoord resetten",
|
||||
"Show Reading Progress": "Leesvoortgang weergeven",
|
||||
"Reading Progress Style": "Stijl van leesvoortgang",
|
||||
"Page Number": "Pagina nummer",
|
||||
"Percentage": "Percentage",
|
||||
"Deleted local copy of the book: {{title}}": "Verwijderde lokale kopie van het boek: {{title}}",
|
||||
"Failed to delete cloud backup of the book: {{title}}": "Verwijderen van cloudback-up van het boek is mislukt: {{title}}",
|
||||
"Failed to delete local copy of the book: {{title}}": "Verwijderen van lokale kopie van het boek is mislukt: {{title}}",
|
||||
"Are you sure to delete the local copy of the selected book?": "Weet je zeker dat je de lokale kopie van het geselecteerde boek wilt verwijderen?",
|
||||
"Remove from Cloud & Device": "Verwijder uit Cloud & Apparaat",
|
||||
"Remove from Cloud Only": "Verwijder alleen uit Cloud",
|
||||
"Remove from Device Only": "Verwijder alleen uit Apparaat",
|
||||
"Disconnected": "Verbroken",
|
||||
"KOReader Sync Settings": "KOReader Sync-instellingen",
|
||||
"Sync Strategy": "Synchronisatiestrategie",
|
||||
"Ask on conflict": "Vraag bij conflict",
|
||||
"Always use latest": "Altijd de nieuwste gebruiken",
|
||||
"Send changes only": "Verzend alleen wijzigingen",
|
||||
"Receive changes only": "Ontvang alleen wijzigingen",
|
||||
"Disabled": "Uitgeschakeld",
|
||||
"Checksum Method": "Checksum-methode",
|
||||
"File Content (recommended)": "Bestandsinhoud (aanbevolen)",
|
||||
"File Name": "Bestandsnaam",
|
||||
"Device Name": "Apparaatnaam",
|
||||
"Sync Tolerance": "Synchronisatietolerantie",
|
||||
"Connect to your KOReader Sync server.": "Verbind met je KOReader Sync-server.",
|
||||
"Server URL": "Server-URL",
|
||||
"Username": "Gebruikersnaam",
|
||||
"Your Username": "Je gebruikersnaam",
|
||||
"Password": "Wachtwoord",
|
||||
"Connect": "Verbinden",
|
||||
"KOReader Sync": "KOReader Sync",
|
||||
"Sync Conflict": "Synchronisatieconflict",
|
||||
"Sync reading progress from \"{{deviceName}}\"?": "Leesvoortgang synchroniseren van \"{{deviceName}}\"?",
|
||||
"another device": "een ander apparaat",
|
||||
"Local Progress": "Lokale voortgang",
|
||||
"Remote Progress": "Externe voortgang",
|
||||
"Page {{page}} of {{total}} ({{percentage}}%)": "Pagina {{page}} van {{total}} ({{percentage}}%)",
|
||||
"Current position": "Huidige positie",
|
||||
"Approximately page {{page}} of {{total}} ({{percentage}}%)": "Ongeveer pagina {{page}} van {{total}} ({{percentage}}%)",
|
||||
"Approximately {{percentage}}%": "Ongeveer {{percentage}}%",
|
||||
"Failed to connect": "Verbinding mislukt",
|
||||
"Sync Server Connected": "Synchronisatie server verbonden",
|
||||
"Precision: {{precision}} digits after the decimal": "Precisie: {{precision}} cijfers na de komma",
|
||||
"Sync as {{userDisplayName}}": "Synchroniseren als {{userDisplayName}}"
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
"Confirm Deletion": "Potwierdź usunięcie",
|
||||
"Copied to notebook": "Skopiowano do notatnika",
|
||||
"Copy": "Kopiuj",
|
||||
"Custom CSS": "Własny CSS",
|
||||
"Dark Mode": "Tryb ciemny",
|
||||
"Default": "Domyślne",
|
||||
"Default Font": "Domyślna czcionka",
|
||||
@@ -28,7 +27,6 @@
|
||||
"Dictionary": "Słownik",
|
||||
"Download Readest": "Pobierz Readest",
|
||||
"Edit": "Edytuj",
|
||||
"Enter your custom CSS here...": "Wprowadź własny CSS tutaj...",
|
||||
"Excerpts": "Fragmenty",
|
||||
"Failed to import book(s): {{filenames}}": "Nie udało się zaimportować książek: {{filenames}}",
|
||||
"Fast": "Szybko",
|
||||
@@ -48,7 +46,6 @@
|
||||
"Horizontal Direction": "Kierunek poziomy",
|
||||
"Hyphenation": "Dzielenie wyrazów",
|
||||
"Import Books": "Importuj książki",
|
||||
"Language:": "Język:",
|
||||
"Layout": "Układ",
|
||||
"Light Mode": "Tryb jasny",
|
||||
"Loading...": "Ładowanie...",
|
||||
@@ -70,8 +67,8 @@
|
||||
"Paging Animation": "Animacja przewracania stron",
|
||||
"Paragraph": "Akapit",
|
||||
"Parallel Read": "Czytanie równoległe",
|
||||
"Published:": "Opublikowano:",
|
||||
"Publisher:": "Wydawca:",
|
||||
"Published": "Opublikowano",
|
||||
"Publisher": "Wydawca",
|
||||
"Reading Progress Synced": "Zsynchronizowano postęp czytania",
|
||||
"Reload Page": "Odśwież stronę",
|
||||
"Reveal in File Explorer": "Pokaż w Eksploratorze plików",
|
||||
@@ -96,16 +93,15 @@
|
||||
"Slow": "Wolno",
|
||||
"Solarized": "Słoneczny",
|
||||
"Speak": "Czytaj",
|
||||
"Subjects:": "Tematy:",
|
||||
"Subjects": "Tematy",
|
||||
"System Fonts": "Czcionki systemowe",
|
||||
"TTS not supported in this device": "TTS nie jest obsługiwane na tym urządzeniu",
|
||||
"Theme Color": "Kolor motywu",
|
||||
"Theme Mode": "Tryb motywu",
|
||||
"Translate": "Tłumacz",
|
||||
"Translated Text": "Przetłumaczony tekst",
|
||||
"Unknown": "Nieznane",
|
||||
"Untitled": "Bez tytułu",
|
||||
"Updated:": "Zaktualizowano:",
|
||||
"Updated": "Zaktualizowano",
|
||||
"Version {{version}}": "Wersja {{version}}",
|
||||
"Vertical Direction": "Kierunek pionowy",
|
||||
"Welcome to your library. You can import your books here and read them anytime.": "Witaj w swojej bibliotece. Możesz tutaj zaimportować swoje książki i czytać je w dowolnym momencie.",
|
||||
@@ -114,8 +110,6 @@
|
||||
"Your Library": "Twoja biblioteka",
|
||||
"TTS not supported for PDF": "TTS nie jest obsługiwane dla plików PDF",
|
||||
"Override Book Font": "Nadpisz czcionkę książki",
|
||||
"Vertical Margins (px)": "Marginesy pionowe (px)",
|
||||
"Horizontal Margins (%)": "Marginesy poziome (%)",
|
||||
"Apply to All Books": "Zastosuj do wszystkich książek",
|
||||
"Apply to This Book": "Zastosuj do tej książki",
|
||||
"Unable to fetch the translation. Try again later.": "Nie udało się pobrać tłumaczenia. Spróbuj ponownie później.",
|
||||
@@ -130,8 +124,6 @@
|
||||
"Failed to download book: {{title}}": "Nie udało się pobrać książki: {{title}}",
|
||||
"Upload Book": "Prześlij książkę",
|
||||
"Auto Upload Books to Cloud": "Automatycznie przesyłaj książki do chmury",
|
||||
"{{percentage}}% of Cloud Storage Used.": "{{percentage}}% przestrzeni dyskowej w chmurze użytej.",
|
||||
"Storage": "Przechowywanie",
|
||||
"Book deleted: {{title}}": "Książka usunięta: {{title}}",
|
||||
"Failed to delete book: {{title}}": "Nie udało się usunąć książki: {{title}}",
|
||||
"Check Updates on Start": "Sprawdź aktualizacje przy starcie",
|
||||
@@ -163,7 +155,7 @@
|
||||
"Create a Password": "Utwórz hasło",
|
||||
"Sign up": "Zarejestruj się",
|
||||
"Signing up...": "Rejestracja...",
|
||||
"Don’t have an account? Sign up": "Nie masz konta? Zarejestruj się",
|
||||
"Don't have an account? Sign up": "Nie masz konta? Zarejestruj się",
|
||||
"Check your email for the confirmation link": "Sprawdź swój email, aby uzyskać link potwierdzający",
|
||||
"Signing in ...": "Logowanie ...",
|
||||
"Send a magic link email": "Wyślij link magiczny na email",
|
||||
@@ -187,35 +179,13 @@
|
||||
"Sign in with GitHub": "Zaloguj się za pomocą GitHub",
|
||||
"Account": "Konto",
|
||||
"Failed to delete user. Please try again later.": "Nie udało się usunąć użytkownika. Spróbuj ponownie później.",
|
||||
"Unlimited Offline/Online Reading": "Nieograniczone czytanie offline/online",
|
||||
"Unlimited Cloud Sync Devices": "Nieograniczona liczba urządzeń synchronizowanych w chmurze",
|
||||
"Essential Text-to-Speech Voices": "Podstawowe głosy zamiany tekstu na mowę",
|
||||
"DeepL Free Access": "Darmowy dostęp do DeepL",
|
||||
"Community Support": "Wsparcie społeczności",
|
||||
"500 MB Cloud Sync Space": "500 MB przestrzeni synchronizacji w chmurze",
|
||||
"Includes All Free Tier Benefits": "Zawiera wszystkie korzyści darmowego planu",
|
||||
"AI Summaries": "Podsumowania AI",
|
||||
"AI Translations": "Tłumaczenia AI",
|
||||
"Priority Support": "Priorytetowe wsparcie",
|
||||
"DeepL Pro Access": "Dostęp do DeepL Pro",
|
||||
"Expanded Text-to-Speech Voices": "Rozszerzone głosy zamiany tekstu na mowę",
|
||||
"2000 MB Cloud Sync Space": "2000 MB przestrzeni synchronizacji w chmurze",
|
||||
"Includes All Plus Tier Benefits": "Zawiera wszystkie korzyści planu Plus",
|
||||
"Unlimited AI Summaries": "Nieograniczone podsumowania AI",
|
||||
"Unlimited AI Translations": "Nieograniczone tłumaczenia AI",
|
||||
"Unlimited DeepL Pro Access": "Nieograniczony dostęp do DeepL Pro",
|
||||
"Advanced AI Tools": "Zaawansowane narzędzia AI",
|
||||
"Early Feature Access": "Wczesny dostęp do nowych funkcji",
|
||||
"10 GB Cloud Sync Space": "10 GB przestrzeni synchronizacji w chmurze",
|
||||
"Loading profile...": "Ładowanie profilu...",
|
||||
"Features": "Funkcje",
|
||||
"Delete Account": "Usuń konto",
|
||||
"Delete Your Account?": "Usunąć Twoje konto?",
|
||||
"This action cannot be undone. All your data in the cloud will be permanently deleted.": "Tej operacji nie można cofnąć. Wszystkie Twoje dane w chmurze zostaną trwale usunięte.",
|
||||
"Delete Permanently": "Usuń trwale",
|
||||
"Free Tier": "Plan darmowy",
|
||||
"Plus Tier": "Plan Plus",
|
||||
"Pro Tier": "Plan Pro",
|
||||
"RTL Direction": "Kierunek RTL",
|
||||
"Maximum Column Height": "Maksymalna wysokość kolumny",
|
||||
"Maximum Column Width": "Maksymalna szerokość kolumny",
|
||||
@@ -266,13 +236,13 @@
|
||||
"Clear Search": "Wyczyść wyszukiwanie",
|
||||
"Header & Footer": "Nagłówek i stopka",
|
||||
"Apply also in Scrolled Mode": "Zastosuj również w trybie przewijania",
|
||||
"A new version of Readest is Available!": "Nowa wersja Readest jest dostępna!",
|
||||
"A new version of Readest is available!": "Nowa wersja Readest jest dostępna!",
|
||||
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} jest dostępny (zainstalowana wersja {{currentVersion}}).",
|
||||
"Download and install now?": "Pobierz i zainstaluj teraz?",
|
||||
"Downloading {{downloaded}} of {{contentLength}}": "Pobieranie {{downloaded}} z {{contentLength}}",
|
||||
"Download finished": "Pobieranie zakończone",
|
||||
"DOWNLOAD & INSTALL": "POBIERZ I ZAINSTALUJ",
|
||||
"Changelog:": "Dziennik zmian:",
|
||||
"Changelog": "Dziennik zmian",
|
||||
"Software Update": "Aktualizacja oprogramowania",
|
||||
"Title": "Tytuł",
|
||||
"Date Read": "Data przeczytania",
|
||||
@@ -281,10 +251,9 @@
|
||||
"Ascending": "Rosnąco",
|
||||
"Descending": "Malejąco",
|
||||
"Sort by...": "Sortuj według...",
|
||||
"Added:": "Data dodania:",
|
||||
"Format:": "Format:",
|
||||
"Added": "Data dodania",
|
||||
"GuanKiapTsingKhai-T": "GuanKiapTsingKhai-T",
|
||||
"Description:": "Opis:",
|
||||
"Description": "Opis",
|
||||
"No description available": "Brak opisu",
|
||||
"List": "Lista",
|
||||
"Grid": "Siatka",
|
||||
@@ -301,7 +270,7 @@
|
||||
"Checking for updates...": "Sprawdzanie aktualizacji...",
|
||||
"Error checking for updates": "Błąd podczas sprawdzania aktualizacji",
|
||||
"Details": "Szczegóły",
|
||||
"File Size:": "Rozmiar pliku:",
|
||||
"File Size": "Rozmiar pliku",
|
||||
"Auto Detect": "Automatyczne wykrywanie",
|
||||
"Next Section": "Następna sekcja",
|
||||
"Previous Section": "Poprzednia sekcja",
|
||||
@@ -346,14 +315,12 @@
|
||||
"Synced at {{time}}": "Sync: {{time}}",
|
||||
"Never synced": "Brak synchronizacji",
|
||||
"Show Remaining Time": "Pokazuj pozostały czas",
|
||||
"Show Page Number": "Pokaż numer strony",
|
||||
"{{time}} min left in chapter": "{{time}} min pozostało do końca rozdziału",
|
||||
"Override Book Color": "Nadpisz kolor książki",
|
||||
"Login Required": "Wymagane logowanie",
|
||||
"Quota Exceeded": "Limit przekroczony",
|
||||
"{{percentage}}% of Daily Translation Characters Used.": "{{percentage}}% użytych znaków tłumaczenia dziennego.",
|
||||
"Translation Characters": "Znaki tłumaczenia",
|
||||
"Daily translation quota reached. Select another translate service to proceed.": "Przekroczono dzienny limit znaków tłumaczenia. Wybierz inną usługę tłumaczenia, aby kontynuować.",
|
||||
"{{engine}}: {{count}} voices_one": "{{engine}}: {{count}} głos",
|
||||
"{{engine}}: {{count}} voices_few": "{{engine}}: {{count}} głosy",
|
||||
"{{engine}}: {{count}} voices_many": "{{engine}}: {{count}} głosów",
|
||||
@@ -365,5 +332,185 @@
|
||||
"Contact Support": "Skontaktuj się z pomocą techniczną",
|
||||
"Code Highlighting": "Podświetlanie kodu",
|
||||
"Enable Highlighting": "Zezwól na podświetlanie",
|
||||
"Code Language": "Język kodu"
|
||||
"Code Language": "Język kodu",
|
||||
"Top Margin (px)": "Górny margines (px)",
|
||||
"Bottom Margin (px)": "Dolny margines (px)",
|
||||
"Right Margin (px)": "Prawy margines (px)",
|
||||
"Left Margin (px)": "Lewy margines (px)",
|
||||
"Column Gap (%)": "Odstęp między kolumnami (%)",
|
||||
"Always Show Status Bar": "Zawsze pokazuj pasek stanu",
|
||||
"Translation Not Available": "Brak dostępnego tłumaczenia",
|
||||
"Custom Content CSS": "CSS treści",
|
||||
"Enter CSS for book content styling...": "Wprowadź CSS dla stylu treści książki...",
|
||||
"Custom Reader UI CSS": "CSS interfejsu",
|
||||
"Enter CSS for reader interface styling...": "Wprowadź CSS dla stylu interfejsu czytnika...",
|
||||
"Crop": "Przytnij",
|
||||
"Book Covers": "Okładki książek",
|
||||
"Fit": "Pasuje",
|
||||
"Reset {{settings}}": "Zresetuj {{settings}}",
|
||||
"Reset Settings": "Zresetuj ustawienia",
|
||||
"{{count}} pages left in chapter_one": "Pozostała {{count}} strona w rozdziale",
|
||||
"{{count}} pages left in chapter_few": "Pozostały {{count}} strony w rozdziale",
|
||||
"{{count}} pages left in chapter_many": "Pozostało {{count}} stron w rozdziale",
|
||||
"{{count}} pages left in chapter_other": "Pozostało {{count}} stron w rozdziale",
|
||||
"Show Remaining Pages": "Pokaż pozostałe strony",
|
||||
"Source Han Serif CN VF": "Source Han Serif",
|
||||
"Huiwen-mincho": "Huiwen Mincho",
|
||||
"KingHwa_OldSong": "KingHwa Song",
|
||||
"Manage Subscription": "Zarządzaj subskrypcją",
|
||||
"Coming Soon": "Wkrótce dostępne",
|
||||
"Upgrade to {{plan}}": "Uaktualnij do {{plan}}",
|
||||
"Upgrade to Plus or Pro": "Uaktualnij do Plus lub Pro",
|
||||
"Current Plan": "Obecny plan",
|
||||
"Plan Limits": "Limity planu",
|
||||
"Processing your payment...": "Przetwarzanie płatności...",
|
||||
"Please wait while we confirm your subscription.": "Proszę czekać, potwierdzamy subskrypcję.",
|
||||
"Payment Processing": "Przetwarzanie płatności",
|
||||
"Your payment is being processed. This usually takes a few moments.": "Twoja płatność jest przetwarzana. Zwykle trwa to kilka chwil.",
|
||||
"Payment Failed": "Płatność nie powiodła się",
|
||||
"We couldn't process your subscription. Please try again or contact support if the issue persists.": "Nie udało się przetworzyć subskrypcji. Spróbuj ponownie lub skontaktuj się z pomocą techniczną, jeśli problem będzie się powtarzać.",
|
||||
"Back to Profile": "Powrót do profilu",
|
||||
"Subscription Successful!": "Subskrypcja zakończona sukcesem!",
|
||||
"Thank you for your subscription! Your payment has been processed successfully.": "Dziękujemy za subskrypcję! Twoja płatność została pomyślnie przetworzona.",
|
||||
"Email:": "E-mail:",
|
||||
"Plan:": "Plan:",
|
||||
"Amount:": "Kwota:",
|
||||
"Subscription ID:": "ID subskrypcji:",
|
||||
"Go to Library": "Przejdź do biblioteki",
|
||||
"Need help? Contact our support team at support@readest.com": "Potrzebujesz pomocy? Skontaktuj się z naszym zespołem wsparcia: support@readest.com",
|
||||
"Free Plan": "Bezpłatny plan",
|
||||
"month": "miesiąc",
|
||||
"AI Translations (per day)": "Tłumaczenia AI (na dzień)",
|
||||
"Plus Plan": "Plan Plus",
|
||||
"Includes All Free Plan Benefits": "Zawiera wszystkie korzyści planu darmowego",
|
||||
"Pro Plan": "Plan Pro",
|
||||
"More AI Translations": "Więcej tłumaczeń AI",
|
||||
"Complete Your Subscription": "Ukończ subskrypcję",
|
||||
"{{percentage}}% of Cloud Sync Space Used.": "Użyto {{percentage}}% przestrzeni synchronizacji w chmurze.",
|
||||
"Cloud Sync Storage": "Miejsce w chmurze",
|
||||
"Disable": "Wyłącz",
|
||||
"Enable": "Włącz",
|
||||
"Upgrade to Readest Premium": "Uaktualnij do Readest Premium",
|
||||
"Show Source Text": "Pokaż tekst źródłowy",
|
||||
"Cross-Platform Sync": "Synchronizacja Między Platformami",
|
||||
"Seamlessly sync your library, progress, highlights, and notes across all your devices—never lose your place again.": "Synchronizuj płynnie swoją bibliotekę, postęp, zaznaczenia i notatki na wszystkich urządzeniach—nigdy więcej nie zgub swojego miejsca.",
|
||||
"Customizable Reading": "Personalizowane Czytanie",
|
||||
"Personalize every detail with adjustable fonts, layouts, themes, and advanced display settings for the perfect reading experience.": "Personalizuj każdy szczegół dzięki regulowanym czcionkom, układom, motywom i zaawansowanym ustawieniom wyświetlania dla idealnego doświadczenia czytania.",
|
||||
"AI Read Aloud": "AI Czytanie Na Głos",
|
||||
"Enjoy hands-free reading with natural-sounding AI voices that bring your books to life.": "Ciesz się czytaniem bez użycia rąk dzięki naturalnie brzmiącym głosom AI, które ożywiają twoje książki.",
|
||||
"AI Translations": "Tłumaczenia AI",
|
||||
"Translate any text instantly with the power of Google, Azure, or DeepL—understand content in any language.": "Tłumacz dowolny tekst natychmiast dzięki mocy Google, Azure lub DeepL—rozumiej treści w każdym języku.",
|
||||
"Connect with fellow readers and get help fast in our friendly community channels.": "Połącz się z innymi czytelnikami i uzyskaj szybką pomoc na naszych przyjaznych kanałach społeczności.",
|
||||
"Unlimited AI Read Aloud Hours": "Nielimitowane Godziny AI Czytania Na Głos",
|
||||
"Listen without limits—convert as much text as you like into immersive audio.": "Słuchaj bez ograniczeń—przekształć tyle tekstu, ile chcesz, w wciągające audio.",
|
||||
"Unlock enhanced translation capabilities with more daily usage and advanced options.": "Odblokuj ulepszone możliwości tłumaczenia z większym dziennym użyciem i zaawansowanymi opcjami.",
|
||||
"DeepL Pro Access": "Dostęp do DeepL Pro",
|
||||
"Enjoy faster responses and dedicated assistance whenever you need help.": "Ciesz się szybszymi odpowiedziami i dedykowaną pomocą, kiedy tylko potrzebujesz wsparcia.",
|
||||
"Daily translation quota reached. Upgrade your plan to continue using AI translations.": "Osiągnięto dzienny limit tłumaczeń. Uaktualnij swój plan, aby kontynuować korzystanie z tłumaczeń AI.",
|
||||
"Includes All Plus Plan Benefits": "Zawiera Wszystkie Korzyści Planu Plus",
|
||||
"Early Feature Access": "Wcześniejszy Dostęp do Funkcji",
|
||||
"Be the first to explore new features, updates, and innovations before anyone else.": "Odkrywaj nowe funkcje, aktualizacje i innowacje przed innymi.",
|
||||
"Advanced AI Tools": "Zaawansowane Narzędzia AI",
|
||||
"Harness powerful AI tools for smarter reading, translation, and content discovery.": "Wykorzystuj potężne narzędzia AI do inteligentnego czytania, tłumaczenia i odkrywania treści.",
|
||||
"Translate up to 100,000 characters daily with the most accurate translation engine available.": "Tłumacz do 100 000 znaków dziennie z najdokładniejszą dostępną maszyną tłumaczeniową.",
|
||||
"Translate up to 500,000 characters daily with the most accurate translation engine available.": "Tłumacz do 500 000 znaków dziennie z najdokładniejszą dostępną maszyną tłumaczeniową.",
|
||||
"Securely store and access your entire reading collection with up to 5 GB of cloud storage.": "Bezpiecznie przechowuj i uzyskuj dostęp do całej kolekcji czytelniczej z do 5 GB pamięci w chmurze.",
|
||||
"Securely store and access your entire reading collection with up to 20 GB of cloud storage.": "Bezpiecznie przechowuj i uzyskuj dostęp do całej kolekcji czytelniczej z do 20 GB pamięci w chmurze.",
|
||||
"Deleted cloud backup of the book: {{title}}": "Usunięto kopię zapasową książki w chmurze: {{title}}",
|
||||
"Are you sure to delete the cloud backup of the selected book?": "Czy na pewno chcesz usunąć kopię zapasową chmury wybranej książki?",
|
||||
"What's New in Readest": "Co nowego w Readest",
|
||||
"Enter book title": "Wprowadź tytuł książki",
|
||||
"Subtitle": "Podtytuł",
|
||||
"Enter book subtitle": "Wprowadź podtytuł książki",
|
||||
"Enter author name": "Wprowadź imię autora",
|
||||
"Series": "Seria",
|
||||
"Enter series name": "Wprowadź nazwę serii",
|
||||
"Series Index": "Numer w serii",
|
||||
"Enter series index": "Wprowadź numer w serii",
|
||||
"Total in Series": "Łącznie w serii",
|
||||
"Enter total books in series": "Wprowadź łączną liczbę książek w serii",
|
||||
"Enter publisher": "Wprowadź wydawcę",
|
||||
"Publication Date": "Data publikacji",
|
||||
"Identifier": "Identyfikator",
|
||||
"Enter book description": "Wprowadź opis książki",
|
||||
"Change cover image": "Zmień obraz okładki",
|
||||
"Replace": "Zastąp",
|
||||
"Unlock cover": "Odblokuj okładkę",
|
||||
"Lock cover": "Zablokuj okładkę",
|
||||
"Auto-Retrieve Metadata": "Automatycznie pobierz metadane",
|
||||
"Auto-Retrieve": "Automatyczne pobieranie",
|
||||
"Unlock all fields": "Odblokuj wszystkie pola",
|
||||
"Unlock All": "Odblokuj wszystko",
|
||||
"Lock all fields": "Zablokuj wszystkie pola",
|
||||
"Lock All": "Zablokuj wszystko",
|
||||
"Reset": "Resetuj",
|
||||
"Edit Metadata": "Edytuj metadane",
|
||||
"Locked": "Zablokowane",
|
||||
"Select Metadata Source": "Wybierz źródło metadanych",
|
||||
"Keep manual input": "Zachowaj wprowadzenie ręczne",
|
||||
"Google Books": "Google Books",
|
||||
"Open Library": "Open Library",
|
||||
"Fiction, Science, History": "Beletrystyka, Nauka, Historia",
|
||||
"Select Cover Image": "Zaznacz obraz okładki",
|
||||
"Open Book in New Window": "Otwórz książkę w nowym oknie",
|
||||
"Voices for {{lang}}": "{{lang}} Głosy",
|
||||
"Yandex Translate": "Yandex Tłumacz",
|
||||
"YYYY or YYYY-MM-DD": "YYYY lub YYYY-MM-DD",
|
||||
"Restore Purchase": "Przywróć zakup",
|
||||
"No purchases found to restore.": "Nie znaleziono zakupów do przywrócenia.",
|
||||
"Failed to restore purchases.": "Nie udało się przywrócić zakupów.",
|
||||
"Failed to manage subscription.": "Nie udało się zarządzać subskrypcją.",
|
||||
"Failed to load subscription plans.": "Nie udało się załadować planów subskrypcyjnych.",
|
||||
"year": "rok",
|
||||
"Failed to create checkout session": "Nie udało się utworzyć sesji zakupu",
|
||||
"Storage": "Przechowywanie",
|
||||
"Terms of Service": "Warunki korzystania z usługi",
|
||||
"Privacy Policy": "Polityka prywatności",
|
||||
"Disable Double Click": "Wyłącz podwójne kliknięcie",
|
||||
"TTS not supported for this document": "TTS nie jest obsługiwane dla tego dokumentu",
|
||||
"Reset Password": "Zresetuj hasło",
|
||||
"Show Reading Progress": "Pokazuj postęp czytania",
|
||||
"Reading Progress Style": "Styl postępu czytania",
|
||||
"Page Number": "Numer strony",
|
||||
"Percentage": "Procent",
|
||||
"Deleted local copy of the book: {{title}}": "Usunięto lokalną kopię książki: {{title}}",
|
||||
"Failed to delete cloud backup of the book: {{title}}": "Nie udało się usunąć kopii zapasowej w chmurze książki: {{title}}",
|
||||
"Failed to delete local copy of the book: {{title}}": "Nie udało się usunąć lokalnej kopii książki: {{title}}",
|
||||
"Are you sure to delete the local copy of the selected book?": "Czy na pewno chcesz usunąć lokalną kopię wybranej książki?",
|
||||
"Remove from Cloud & Device": "Usuń z chmury i urządzenia",
|
||||
"Remove from Cloud Only": "Usuń tylko z chmury",
|
||||
"Remove from Device Only": "Usuń tylko z urządzenia",
|
||||
"Disconnected": "Rozłączono",
|
||||
"KOReader Sync Settings": "Ustawienia synchronizacji KOReader",
|
||||
"Sync Strategy": "Strategia synchronizacji",
|
||||
"Ask on conflict": "Pytaj w przypadku konfliktu",
|
||||
"Always use latest": "Zawsze używaj najnowszej",
|
||||
"Send changes only": "Wyślij tylko zmiany",
|
||||
"Receive changes only": "Odbierz tylko zmiany",
|
||||
"Disabled": "Wyłączone",
|
||||
"Checksum Method": "Metoda sumy kontrolnej",
|
||||
"File Content (recommended)": "Zawartość pliku (zalecane)",
|
||||
"File Name": "Nazwa pliku",
|
||||
"Device Name": "Nazwa urządzenia",
|
||||
"Sync Tolerance": "Tolerancja synchronizacji",
|
||||
"Connect to your KOReader Sync server.": "Połącz z serwerem synchronizacji KOReader.",
|
||||
"Server URL": "Adres URL serwera",
|
||||
"Username": "Nazwa użytkownika",
|
||||
"Your Username": "Twoja nazwa użytkownika",
|
||||
"Password": "Hasło",
|
||||
"Connect": "Połącz",
|
||||
"KOReader Sync": "Synchronizacja KOReader",
|
||||
"Sync Conflict": "Konflikt synchronizacji",
|
||||
"Sync reading progress from \"{{deviceName}}\"?": "Synchronizować postęp czytania z \"{{deviceName}}\"?",
|
||||
"another device": "inne urządzenie",
|
||||
"Local Progress": "Postęp lokalny",
|
||||
"Remote Progress": "Postęp zdalny",
|
||||
"Page {{page}} of {{total}} ({{percentage}}%)": "Strona {{page}} z {{total}} ({{percentage}}%)",
|
||||
"Current position": "Bieżąca pozycja",
|
||||
"Approximately page {{page}} of {{total}} ({{percentage}}%)": "Około strona {{page}} z {{total}} ({{percentage}}%)",
|
||||
"Approximately {{percentage}}%": "Około {{percentage}}%",
|
||||
"Failed to connect": "Nie udało się połączyć",
|
||||
"Sync Server Connected": "Serwer synchronizacji połączony",
|
||||
"Precision: {{precision}} digits after the decimal": "Precyzja: {{precision}} cyfr po przecinku",
|
||||
"Sync as {{userDisplayName}}": "Synchronizuj jako {{userDisplayName}}"
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
"Confirm Deletion": "Confirmar Exclusão",
|
||||
"Copied to notebook": "Copiado para o caderno",
|
||||
"Copy": "Copiar",
|
||||
"Custom CSS": "CSS Personalizado",
|
||||
"Dark Mode": "Modo Escuro",
|
||||
"Default": "Padrão",
|
||||
"Default Font": "Fonte Padrão",
|
||||
@@ -28,7 +27,6 @@
|
||||
"Dictionary": "Dicionário",
|
||||
"Download Readest": "Baixar Readest",
|
||||
"Edit": "Editar",
|
||||
"Enter your custom CSS here...": "Insira seu CSS personalizado aqui...",
|
||||
"Excerpts": "Trechos",
|
||||
"Failed to import book(s): {{filenames}}": "Falha ao importar livro(s): {{filenames}}",
|
||||
"Fast": "Rápido",
|
||||
@@ -48,7 +46,6 @@
|
||||
"Horizontal Direction": "Direção Horizontal",
|
||||
"Hyphenation": "Hifenização",
|
||||
"Import Books": "Importar Livros",
|
||||
"Language:": "Idioma:",
|
||||
"Layout": "Layout",
|
||||
"Light Mode": "Modo Claro",
|
||||
"Loading...": "Carregando...",
|
||||
@@ -70,8 +67,8 @@
|
||||
"Paging Animation": "Animação de Página",
|
||||
"Paragraph": "Parágrafo",
|
||||
"Parallel Read": "Leitura Paralela",
|
||||
"Published:": "Publicado:",
|
||||
"Publisher:": "Editora:",
|
||||
"Published": "Publicado",
|
||||
"Publisher": "Editora",
|
||||
"Reading Progress Synced": "Progresso de leitura sincronizado",
|
||||
"Reload Page": "Recarregar Página",
|
||||
"Reveal in File Explorer": "Mostrar no Explorador de Arquivos",
|
||||
@@ -96,16 +93,15 @@
|
||||
"Slow": "Lento",
|
||||
"Solarized": "Solarizado",
|
||||
"Speak": "Falar",
|
||||
"Subjects:": "Assuntos:",
|
||||
"Subjects": "Assuntos",
|
||||
"System Fonts": "Fontes do Sistema",
|
||||
"TTS not supported in this device": "TTS não suportado neste dispositivo",
|
||||
"Theme Color": "Cor do Tema",
|
||||
"Theme Mode": "Modo do Tema",
|
||||
"Translate": "Traduzir",
|
||||
"Translated Text": "Texto Traduzido",
|
||||
"Unknown": "Desconhecido",
|
||||
"Untitled": "Sem Título",
|
||||
"Updated:": "Atualizado:",
|
||||
"Updated": "Atualizado",
|
||||
"Version {{version}}": "Versão {{version}}",
|
||||
"Vertical Direction": "Direção Vertical",
|
||||
"Welcome to your library. You can import your books here and read them anytime.": "Bem-vindo à sua biblioteca. Você pode importar seus livros aqui e lê-los a qualquer momento.",
|
||||
@@ -114,8 +110,6 @@
|
||||
"Your Library": "Sua Biblioteca",
|
||||
"TTS not supported for PDF": "TTS não suportado para PDF",
|
||||
"Override Book Font": "Sobrescrever Fonte do Livro",
|
||||
"Vertical Margins (px)": "Margens Verticais (px)",
|
||||
"Horizontal Margins (%)": "Margens Horizontais (%)",
|
||||
"Apply to All Books": "Aplicar a todos os livros",
|
||||
"Apply to This Book": "Aplicar a este livro",
|
||||
"Unable to fetch the translation. Try again later.": "Não foi possível buscar a tradução. Tente novamente mais tarde.",
|
||||
@@ -130,8 +124,6 @@
|
||||
"Failed to download book: {{title}}": "Falha ao baixar livro: {{title}}",
|
||||
"Upload Book": "Enviar Livro",
|
||||
"Auto Upload Books to Cloud": "Enviar Livros para a Nuvem Automaticamente",
|
||||
"{{percentage}}% of Cloud Storage Used.": "{{percentage}}% do Armazenamento na Nuvem Usado.",
|
||||
"Storage": "Armazenamento",
|
||||
"Book deleted: {{title}}": "Livro excluído: {{title}}",
|
||||
"Failed to delete book: {{title}}": "Falha ao excluir livro: {{title}}",
|
||||
"Check Updates on Start": "Verificar Atualizações ao Iniciar",
|
||||
@@ -163,7 +155,7 @@
|
||||
"Create a Password": "Criar uma senha",
|
||||
"Sign up": "Cadastrar-se",
|
||||
"Signing up...": "Cadastrando...",
|
||||
"Don’t have an account? Sign up": "Não tem uma conta? Cadastre-se",
|
||||
"Don't have an account? Sign up": "Não tem uma conta? Cadastre-se",
|
||||
"Check your email for the confirmation link": "Verifique seu e-mail para o link de confirmação",
|
||||
"Signing in ...": "Entrando ...",
|
||||
"Send a magic link email": "Enviar um link mágico por e-mail",
|
||||
@@ -187,35 +179,13 @@
|
||||
"Sign in with GitHub": "Entrar com o GitHub",
|
||||
"Account": "Conta",
|
||||
"Failed to delete user. Please try again later.": "Falha ao excluir usuário. Por favor, tente novamente mais tarde.",
|
||||
"Unlimited Offline/Online Reading": "Leitura offline/online ilimitada",
|
||||
"Unlimited Cloud Sync Devices": "Sincronização ilimitada de dispositivos na nuvem",
|
||||
"Essential Text-to-Speech Voices": "Vozes essenciais de texto para fala",
|
||||
"DeepL Free Access": "Acesso gratuito ao DeepL",
|
||||
"Community Support": "Suporte da comunidade",
|
||||
"500 MB Cloud Sync Space": "500 MB de espaço de sincronização na nuvem",
|
||||
"Includes All Free Tier Benefits": "Inclui todos os benefícios do plano gratuito",
|
||||
"AI Summaries": "Resumos de IA",
|
||||
"AI Translations": "Traduções de IA",
|
||||
"Priority Support": "Suporte prioritário",
|
||||
"DeepL Pro Access": "Acesso ao DeepL Pro",
|
||||
"Expanded Text-to-Speech Voices": "Vozes ampliadas de texto para fala",
|
||||
"2000 MB Cloud Sync Space": "2000 MB de espaço de sincronização na nuvem",
|
||||
"Includes All Plus Tier Benefits": "Inclui todos os benefícios do plano Plus",
|
||||
"Unlimited AI Summaries": "Resumos de IA ilimitados",
|
||||
"Unlimited AI Translations": "Traduções de IA ilimitadas",
|
||||
"Unlimited DeepL Pro Access": "Acesso ilimitado ao DeepL Pro",
|
||||
"Advanced AI Tools": "Ferramentas avançadas de IA",
|
||||
"Early Feature Access": "Acesso antecipado a recursos",
|
||||
"10 GB Cloud Sync Space": "10 GB de espaço de sincronização na nuvem",
|
||||
"Loading profile...": "Carregando perfil...",
|
||||
"Features": "Recursos",
|
||||
"Delete Account": "Excluir conta",
|
||||
"Delete Your Account?": "Excluir sua conta?",
|
||||
"This action cannot be undone. All your data in the cloud will be permanently deleted.": "Esta ação não pode ser desfeita. Todos os seus dados na nuvem serão excluídos permanentemente.",
|
||||
"Delete Permanently": "Excluir permanentemente",
|
||||
"Free Tier": "Plano gratuito",
|
||||
"Plus Tier": "Plano Plus",
|
||||
"Pro Tier": "Plano Pro",
|
||||
"RTL Direction": "Direção RTL",
|
||||
"Maximum Column Height": "Altura Máxima da Coluna",
|
||||
"Maximum Column Width": "Largura Máxima da Coluna",
|
||||
@@ -266,13 +236,13 @@
|
||||
"Clear Search": "Limpar Pesquisa",
|
||||
"Header & Footer": "Cabeçalho e Rodapé",
|
||||
"Apply also in Scrolled Mode": "Aplicar também no Modo Rolado",
|
||||
"A new version of Readest is Available!": "Uma nova versão do Readest está disponível!",
|
||||
"A new version of Readest is available!": "Uma nova versão do Readest está disponível!",
|
||||
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} está disponível (versão instalada {{currentVersion}}).",
|
||||
"Download and install now?": "Baixar e instalar agora?",
|
||||
"Downloading {{downloaded}} of {{contentLength}}": "Baixando {{downloaded}} de {{contentLength}}",
|
||||
"Download finished": "Download concluído",
|
||||
"DOWNLOAD & INSTALL": "BAIXAR E INSTALAR",
|
||||
"Changelog:": "Notas de versão:",
|
||||
"Changelog": "Notas de versão",
|
||||
"Software Update": "Atualização de Software",
|
||||
"Title": "Título",
|
||||
"Date Read": "Data de leitura",
|
||||
@@ -281,10 +251,9 @@
|
||||
"Ascending": "Crescente",
|
||||
"Descending": "Decrescente",
|
||||
"Sort by...": "Ordenar por...",
|
||||
"Added:": "Adicionado:",
|
||||
"Format:": "Formato:",
|
||||
"Added": "Adicionado",
|
||||
"GuanKiapTsingKhai-T": "GuanKiapTsingKhai-T",
|
||||
"Description:": "Descrição:",
|
||||
"Description": "Descrição",
|
||||
"No description available": "Nenhuma descrição disponível",
|
||||
"List": "Lista",
|
||||
"Grid": "Grade",
|
||||
@@ -301,7 +270,7 @@
|
||||
"Checking for updates...": "Verificando atualizações...",
|
||||
"Error checking for updates": "Erro ao verificar atualizações",
|
||||
"Details": "Detalhes",
|
||||
"File Size:": "Tamanho do arquivo:",
|
||||
"File Size": "Tamanho do arquivo",
|
||||
"Auto Detect": "Detectar Automaticamente",
|
||||
"Next Section": "Próxima seção",
|
||||
"Previous Section": "Seção anterior",
|
||||
@@ -344,14 +313,12 @@
|
||||
"Synced at {{time}}": "Sincronizado às {{time}}",
|
||||
"Never synced": "Nunca sincronizado",
|
||||
"Show Remaining Time": "Mostrar Tempo Restante",
|
||||
"Show Page Number": "Mostrar Número da Página",
|
||||
"{{time}} min left in chapter": "{{time}} min restantes no capítulo",
|
||||
"Override Book Color": "Substituir Cor do Livro",
|
||||
"Login Required": "Login necessário",
|
||||
"Quota Exceeded": "Limite excedido",
|
||||
"{{percentage}}% of Daily Translation Characters Used.": "{{percentage}}% dos caracteres de tradução diários usados.",
|
||||
"Translation Characters": "Caracteres de Tradução",
|
||||
"Daily translation quota reached. Select another translate service to proceed.": "Quota diária de tradução atingida. Selecione outro serviço de tradução para continuar.",
|
||||
"{{engine}}: {{count}} voices_one": "{{engine}}: {{count}} voz",
|
||||
"{{engine}}: {{count}} voices_many": "{{engine}}: {{count}} vozes",
|
||||
"{{engine}}: {{count}} voices_other": "{{engine}}: {{count}} vozes",
|
||||
@@ -362,5 +329,184 @@
|
||||
"Contact Support": "Contato com Suporte",
|
||||
"Code Highlighting": "Realce de Código",
|
||||
"Enable Highlighting": "Ativar Realce",
|
||||
"Code Language": "Idioma do Código"
|
||||
"Code Language": "Idioma do Código",
|
||||
"Top Margin (px)": "Margem superior (px)",
|
||||
"Bottom Margin (px)": "Margem inferior (px)",
|
||||
"Right Margin (px)": "Margem direita (px)",
|
||||
"Left Margin (px)": "Margem esquerda (px)",
|
||||
"Column Gap (%)": "Espaço entre colunas (%)",
|
||||
"Always Show Status Bar": "Exibir sempre a barra de status",
|
||||
"Translation Not Available": "Tradução não disponível",
|
||||
"Custom Content CSS": "CSS do conteúdo",
|
||||
"Enter CSS for book content styling...": "Insira o CSS para o estilo do conteúdo do livro...",
|
||||
"Custom Reader UI CSS": "CSS da interface",
|
||||
"Enter CSS for reader interface styling...": "Insira o CSS para o estilo da interface de leitura...",
|
||||
"Crop": "Cortar",
|
||||
"Book Covers": "Capas de Livros",
|
||||
"Fit": "Encaixar",
|
||||
"Reset {{settings}}": "Redefinir {{settings}}",
|
||||
"Reset Settings": "Redefinir Configurações",
|
||||
"{{count}} pages left in chapter_one": "Falta {{count}} página neste capítulo",
|
||||
"{{count}} pages left in chapter_many": "Faltam {{count}} páginas neste capítulo",
|
||||
"{{count}} pages left in chapter_other": "Faltam {{count}} páginas neste capítulo",
|
||||
"Show Remaining Pages": "Mostrar páginas restantes",
|
||||
"Source Han Serif CN VF": "Source Han Serif",
|
||||
"Huiwen-mincho": "Huiwen Mincho",
|
||||
"KingHwa_OldSong": "KingHwa Song",
|
||||
"Manage Subscription": "Gerenciar assinatura",
|
||||
"Coming Soon": "Em breve",
|
||||
"Upgrade to {{plan}}": "Atualizar para {{plan}}",
|
||||
"Upgrade to Plus or Pro": "Atualizar para Plus ou Pro",
|
||||
"Current Plan": "Plano atual",
|
||||
"Plan Limits": "Limites do plano",
|
||||
"Processing your payment...": "Processando seu pagamento...",
|
||||
"Please wait while we confirm your subscription.": "Por favor, aguarde enquanto confirmamos sua assinatura.",
|
||||
"Payment Processing": "Processando pagamento",
|
||||
"Your payment is being processed. This usually takes a few moments.": "Seu pagamento está sendo processado. Isso normalmente leva alguns instantes.",
|
||||
"Payment Failed": "Falha no pagamento",
|
||||
"We couldn't process your subscription. Please try again or contact support if the issue persists.": "Não foi possível processar sua assinatura. Tente novamente ou entre em contato com o suporte se o problema persistir.",
|
||||
"Back to Profile": "Voltar ao perfil",
|
||||
"Subscription Successful!": "Assinatura realizada com sucesso!",
|
||||
"Thank you for your subscription! Your payment has been processed successfully.": "Obrigado pela sua assinatura! Seu pagamento foi processado com sucesso.",
|
||||
"Email:": "E-mail:",
|
||||
"Plan:": "Plano:",
|
||||
"Amount:": "Valor:",
|
||||
"Subscription ID:": "ID da assinatura:",
|
||||
"Go to Library": "Ir para a biblioteca",
|
||||
"Need help? Contact our support team at support@readest.com": "Precisa de ajuda? Entre em contato com nossa equipe de suporte: support@readest.com",
|
||||
"Free Plan": "Plano gratuito",
|
||||
"month": "mês",
|
||||
"AI Translations (per day)": "Traduções com IA (por dia)",
|
||||
"Plus Plan": "Plano Plus",
|
||||
"Includes All Free Plan Benefits": "Inclui todos os benefícios do plano gratuito",
|
||||
"Pro Plan": "Plano Pro",
|
||||
"More AI Translations": "Mais traduções com IA",
|
||||
"Complete Your Subscription": "Complete sua assinatura",
|
||||
"{{percentage}}% of Cloud Sync Space Used.": "{{percentage}}% do espaço de sincronização na nuvem utilizado.",
|
||||
"Cloud Sync Storage": "Armazenamento na Nuvem",
|
||||
"Disable": "Desativar",
|
||||
"Enable": "Ativar",
|
||||
"Upgrade to Readest Premium": "Atualizar para Readest Premium",
|
||||
"Show Source Text": "Mostrar Texto Fonte",
|
||||
"Cross-Platform Sync": "Sincronização Multiplataforma",
|
||||
"Seamlessly sync your library, progress, highlights, and notes across all your devices—never lose your place again.": "Sincronize perfeitamente sua biblioteca, progresso, destaques e notas em todos os seus dispositivos—nunca mais perca seu lugar.",
|
||||
"Customizable Reading": "Leitura Personalizável",
|
||||
"Personalize every detail with adjustable fonts, layouts, themes, and advanced display settings for the perfect reading experience.": "Personalize cada detalhe com fontes ajustáveis, layouts, temas e configurações avançadas de exibição para a experiência de leitura perfeita.",
|
||||
"AI Read Aloud": "Leitura em Voz Alta por IA",
|
||||
"Enjoy hands-free reading with natural-sounding AI voices that bring your books to life.": "Desfrute da leitura sem usar as mãos com vozes de IA com som natural que dão vida aos seus livros.",
|
||||
"AI Translations": "Traduções por IA",
|
||||
"Translate any text instantly with the power of Google, Azure, or DeepL—understand content in any language.": "Traduza qualquer texto instantaneamente com o poder do Google, Azure ou DeepL—entenda conteúdo em qualquer idioma.",
|
||||
"Connect with fellow readers and get help fast in our friendly community channels.": "Conecte-se com outros leitores e obtenha ajuda rapidamente em nossos canais comunitários amigáveis.",
|
||||
"Unlimited AI Read Aloud Hours": "Horas Ilimitadas de Leitura por IA",
|
||||
"Listen without limits—convert as much text as you like into immersive audio.": "Ouça sem limites—converta tanto texto quanto quiser em áudio envolvente.",
|
||||
"Unlock enhanced translation capabilities with more daily usage and advanced options.": "Desbloqueie recursos aprimorados de tradução com mais uso diário e opções avançadas.",
|
||||
"DeepL Pro Access": "Acesso ao DeepL Pro",
|
||||
"Enjoy faster responses and dedicated assistance whenever you need help.": "Desfrute de respostas mais rápidas e assistência dedicada sempre que precisar de ajuda.",
|
||||
"Daily translation quota reached. Upgrade your plan to continue using AI translations.": "Cota diária de tradução atingida. Atualize seu plano para continuar usando traduções por IA.",
|
||||
"Includes All Plus Plan Benefits": "Inclui Todos os Benefícios do Plano Plus",
|
||||
"Early Feature Access": "Acesso Antecipado a Recursos",
|
||||
"Be the first to explore new features, updates, and innovations before anyone else.": "Explore primeiro novos recursos, atualizações e inovações antes de qualquer um.",
|
||||
"Advanced AI Tools": "Ferramentas de IA Avançadas",
|
||||
"Harness powerful AI tools for smarter reading, translation, and content discovery.": "Aproveite ferramentas de IA poderosas para leitura inteligente, tradução e descoberta de conteúdo.",
|
||||
"Translate up to 100,000 characters daily with the most accurate translation engine available.": "Traduza até 100.000 caracteres diariamente com o motor de tradução mais preciso disponível.",
|
||||
"Translate up to 500,000 characters daily with the most accurate translation engine available.": "Traduza até 500.000 caracteres diariamente com o motor de tradução mais preciso disponível.",
|
||||
"Securely store and access your entire reading collection with up to 5 GB of cloud storage.": "Armazene e acesse com segurança sua coleção completa de leitura com até 5 GB de armazenamento na nuvem.",
|
||||
"Securely store and access your entire reading collection with up to 20 GB of cloud storage.": "Armazene e acesse com segurança sua coleção completa de leitura com até 20 GB de armazenamento na nuvem.",
|
||||
"Deleted cloud backup of the book: {{title}}": "Backup na nuvem do livro excluído: {{title}}",
|
||||
"Are you sure to delete the cloud backup of the selected book?": "Tem certeza de que deseja excluir o backup na nuvem do livro selecionado?",
|
||||
"What's New in Readest": "Novidades no Readest",
|
||||
"Enter book title": "Digite o título do livro",
|
||||
"Subtitle": "Subtítulo",
|
||||
"Enter book subtitle": "Digite o subtítulo do livro",
|
||||
"Enter author name": "Digite o nome do autor",
|
||||
"Series": "Série",
|
||||
"Enter series name": "Digite o nome da série",
|
||||
"Series Index": "Número da série",
|
||||
"Enter series index": "Digite o número da série",
|
||||
"Total in Series": "Total na série",
|
||||
"Enter total books in series": "Digite o total de livros na série",
|
||||
"Enter publisher": "Digite a editora",
|
||||
"Publication Date": "Data de publicação",
|
||||
"Identifier": "Identificador",
|
||||
"Enter book description": "Digite a descrição do livro",
|
||||
"Change cover image": "Alterar imagem da capa",
|
||||
"Replace": "Substituir",
|
||||
"Unlock cover": "Desbloquear capa",
|
||||
"Lock cover": "Bloquear capa",
|
||||
"Auto-Retrieve Metadata": "Recuperar metadados automaticamente",
|
||||
"Auto-Retrieve": "Recuperação automática",
|
||||
"Unlock all fields": "Desbloquear todos os campos",
|
||||
"Unlock All": "Desbloquear tudo",
|
||||
"Lock all fields": "Bloquear todos os campos",
|
||||
"Lock All": "Bloquear tudo",
|
||||
"Reset": "Redefinir",
|
||||
"Edit Metadata": "Editar metadados",
|
||||
"Locked": "Bloqueado",
|
||||
"Select Metadata Source": "Selecionar fonte de metadados",
|
||||
"Keep manual input": "Manter entrada manual",
|
||||
"Google Books": "Google Books",
|
||||
"Open Library": "Open Library",
|
||||
"Fiction, Science, History": "Ficção, Ciência, História",
|
||||
"Select Cover Image": "Selecionar imagem da capa",
|
||||
"Open Book in New Window": "Abrir livro em nova janela",
|
||||
"Voices for {{lang}}": "Vozes para {{lang}}",
|
||||
"Yandex Translate": "Yandex Translate",
|
||||
"YYYY or YYYY-MM-DD": "YYYY ou YYYY-MM-DD",
|
||||
"Restore Purchase": "Restaurar Compra",
|
||||
"No purchases found to restore.": "Nenhuma compra encontrada para restaurar.",
|
||||
"Failed to restore purchases.": "Falha ao restaurar compras.",
|
||||
"Failed to manage subscription.": "Falha ao gerenciar assinatura.",
|
||||
"Failed to load subscription plans.": "Falha ao carregar planos de assinatura.",
|
||||
"year": "ano",
|
||||
"Failed to create checkout session": "Falha ao criar sessão de checkout",
|
||||
"Storage": "Armazenamento",
|
||||
"Terms of Service": "Termos de Serviço",
|
||||
"Privacy Policy": "Política de Privacidade",
|
||||
"Disable Double Click": "Desativar Clique Duplo",
|
||||
"TTS not supported for this document": "TTS não suportado para este documento",
|
||||
"Reset Password": "Redefinir Senha",
|
||||
"Show Reading Progress": "Mostrar Progresso de Leitura",
|
||||
"Reading Progress Style": "Estilo de Progresso de Leitura",
|
||||
"Page Number": "Número da Página",
|
||||
"Percentage": "Porcentagem",
|
||||
"Deleted local copy of the book: {{title}}": " cópia local do livro excluída: {{title}}",
|
||||
"Failed to delete cloud backup of the book: {{title}}": "Falha ao excluir backup na nuvem do livro: {{title}}",
|
||||
"Failed to delete local copy of the book: {{title}}": "Falha ao excluir cópia local do livro: {{title}}",
|
||||
"Are you sure to delete the local copy of the selected book?": "Tem certeza de que deseja excluir a cópia local do livro selecionado?",
|
||||
"Remove from Cloud & Device": "Remover da Nuvem & Dispositivo",
|
||||
"Remove from Cloud Only": "Remover apenas da Nuvem",
|
||||
"Remove from Device Only": "Remover apenas do Dispositivo",
|
||||
"Disconnected": "Desconectado",
|
||||
"KOReader Sync Settings": "Configurações de Sincronização KOReader",
|
||||
"Sync Strategy": "Estratégia de Sincronização",
|
||||
"Ask on conflict": "Perguntar em caso de conflito",
|
||||
"Always use latest": "Sempre usar o mais recente",
|
||||
"Send changes only": "Enviar apenas alterações",
|
||||
"Receive changes only": "Receber apenas alterações",
|
||||
"Disabled": "Desativado",
|
||||
"Checksum Method": "Método de Verificação",
|
||||
"File Content (recommended)": "Conteúdo do Arquivo (recomendado)",
|
||||
"File Name": "Nome do Arquivo",
|
||||
"Device Name": "Nome do Dispositivo",
|
||||
"Sync Tolerance": "Tolerância de Sincronização",
|
||||
"Connect to your KOReader Sync server.": "Conectar ao seu servidor KOReader Sync.",
|
||||
"Server URL": "URL do Servidor",
|
||||
"Username": "Nome de Usuário",
|
||||
"Your Username": "Seu Nome de Usuário",
|
||||
"Password": "Senha",
|
||||
"Connect": "Conectar",
|
||||
"KOReader Sync": "KOReader Sync",
|
||||
"Sync Conflict": "Conflito de Sincronização",
|
||||
"Sync reading progress from \"{{deviceName}}\"?": "Sincronizar progresso de leitura de \"{{deviceName}}\"?",
|
||||
"another device": "outro dispositivo",
|
||||
"Local Progress": "Progresso Local",
|
||||
"Remote Progress": "Progresso Remoto",
|
||||
"Page {{page}} of {{total}} ({{percentage}}%)": "Página {{page}} de {{total}} ({{percentage}}%)",
|
||||
"Current position": "Posição Atual",
|
||||
"Approximately page {{page}} of {{total}} ({{percentage}}%)": "Aproximadamente página {{page}} de {{total}} ({{percentage}}%)",
|
||||
"Approximately {{percentage}}%": "Aproximadamente {{percentage}}%",
|
||||
"Failed to connect": "Falha ao conectar",
|
||||
"Sync Server Connected": "Servidor de Sincronização Conectado",
|
||||
"Precision: {{precision}} digits after the decimal": "Precisão: {{precision}} dígitos após a vírgula",
|
||||
"Sync as {{userDisplayName}}": "Sincronizar como {{userDisplayName}}"
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
"Confirm Deletion": "Подтвердить удаление",
|
||||
"Copied to notebook": "Скопировано в блокнот",
|
||||
"Copy": "Копировать",
|
||||
"Custom CSS": "Пользовательский CSS",
|
||||
"Dark Mode": "Тёмная тема",
|
||||
"Default": "По умолчанию",
|
||||
"Default Font": "Шрифт по умолчанию",
|
||||
@@ -28,7 +27,6 @@
|
||||
"Dictionary": "Словарь",
|
||||
"Download Readest": "Скачать Readest",
|
||||
"Edit": "Редактировать",
|
||||
"Enter your custom CSS here...": "Введите ваш пользовательский CSS здесь...",
|
||||
"Excerpts": "Отрывки",
|
||||
"Failed to import book(s): {{filenames}}": "Не удалось импортировать книгу(и): {{filenames}}",
|
||||
"Fast": "Быстро",
|
||||
@@ -48,7 +46,6 @@
|
||||
"Horizontal Direction": "Горизонтальное направление",
|
||||
"Hyphenation": "Перенос слов",
|
||||
"Import Books": "Импорт книг",
|
||||
"Language:": "Язык:",
|
||||
"Layout": "Макет",
|
||||
"Light Mode": "Светлая тема",
|
||||
"Loading...": "Загрузка...",
|
||||
@@ -70,8 +67,8 @@
|
||||
"Paging Animation": "Анимация перелистывания",
|
||||
"Paragraph": "Абзац",
|
||||
"Parallel Read": "Параллельное чтение",
|
||||
"Published:": "Опубликовано:",
|
||||
"Publisher:": "Издатель:",
|
||||
"Published": "Опубликовано",
|
||||
"Publisher": "Издатель",
|
||||
"Reading Progress Synced": "Синхронизирован прогресс чтения",
|
||||
"Reload Page": "Перезагрузить страницу",
|
||||
"Reveal in File Explorer": "Показать в проводнике",
|
||||
@@ -96,16 +93,15 @@
|
||||
"Slow": "Медленно",
|
||||
"Solarized": "Солнечный",
|
||||
"Speak": "Произнести",
|
||||
"Subjects:": "Темы:",
|
||||
"Subjects": "Темы",
|
||||
"System Fonts": "Системные шрифты",
|
||||
"TTS not supported in this device": "TTS не поддерживается на этом устройстве",
|
||||
"Theme Color": "Цвет темы",
|
||||
"Theme Mode": "Режим темы",
|
||||
"Translate": "Перевести",
|
||||
"Translated Text": "Переведённый текст",
|
||||
"Unknown": "Неизвестно",
|
||||
"Untitled": "Без названия",
|
||||
"Updated:": "Обновлено:",
|
||||
"Updated": "Обновлено",
|
||||
"Version {{version}}": "Версия {{version}}",
|
||||
"Vertical Direction": "Вертикальное направление",
|
||||
"Welcome to your library. You can import your books here and read them anytime.": "Добро пожаловать в вашу библиотеку. Вы можете импортировать сюда книги и читать их в любое время.",
|
||||
@@ -114,8 +110,6 @@
|
||||
"Your Library": "Ваша библиотека",
|
||||
"TTS not supported for PDF": "TTS не поддерживается для PDF",
|
||||
"Override Book Font": "Переопределить шрифт книги",
|
||||
"Vertical Margins (px)": "Вертикальные поля (px)",
|
||||
"Horizontal Margins (%)": "Горизонтальные поля (%)",
|
||||
"Apply to All Books": "Применить ко всем книгам",
|
||||
"Apply to This Book": "Применить к этой книге",
|
||||
"Unable to fetch the translation. Try again later.": "Не удалось получить перевод. Попробуйте позже.",
|
||||
@@ -130,8 +124,6 @@
|
||||
"Failed to download book: {{title}}": "Не удалось скачать книгу: {{title}}",
|
||||
"Upload Book": "Загрузить книгу",
|
||||
"Auto Upload Books to Cloud": "Автоматическая загрузка книг в облако",
|
||||
"{{percentage}}% of Cloud Storage Used.": "{{percentage}}% использовано облачного хранилища.",
|
||||
"Storage": "Хранилище",
|
||||
"Book deleted: {{title}}": "Книга удалена: {{title}}",
|
||||
"Failed to delete book: {{title}}": "Не удалось удалить книгу: {{title}}",
|
||||
"Check Updates on Start": "Проверять обновления при запуске",
|
||||
@@ -163,7 +155,7 @@
|
||||
"Create a Password": "Создать пароль",
|
||||
"Sign up": "Зарегистрироваться",
|
||||
"Signing up...": "Регистрация...",
|
||||
"Don’t have an account? Sign up": "Нет аккаунта? Зарегистрируйтесь",
|
||||
"Don't have an account? Sign up": "Нет аккаунта? Зарегистрируйтесь",
|
||||
"Check your email for the confirmation link": "Проверьте свою почту для подтверждающей ссылки",
|
||||
"Signing in ...": "Вход ...",
|
||||
"Send a magic link email": "Отправить магическую ссылку по электронной почте",
|
||||
@@ -187,35 +179,13 @@
|
||||
"Sign in with GitHub": "Войти через GitHub",
|
||||
"Account": "Аккаунт",
|
||||
"Failed to delete user. Please try again later.": "Не удалось удалить пользователя. Пожалуйста, повторите попытку позже.",
|
||||
"Unlimited Offline/Online Reading": "Безлимитное чтение офлайн/онлайн",
|
||||
"Unlimited Cloud Sync Devices": "Неограниченное количество устройств для облачной синхронизации",
|
||||
"Essential Text-to-Speech Voices": "Основные голоса для преобразования текста в речь",
|
||||
"DeepL Free Access": "Бесплатный доступ к DeepL",
|
||||
"Community Support": "Поддержка сообщества",
|
||||
"500 MB Cloud Sync Space": "500 МБ облачного пространства для синхронизации",
|
||||
"Includes All Free Tier Benefits": "Включает все преимущества бесплатного тарифа",
|
||||
"AI Summaries": "AI-резюме",
|
||||
"AI Translations": "AI-переводы",
|
||||
"Priority Support": "Приоритетная поддержка",
|
||||
"DeepL Pro Access": "Доступ к DeepL Pro",
|
||||
"Expanded Text-to-Speech Voices": "Расширенный набор голосов для преобразования текста в речь",
|
||||
"2000 MB Cloud Sync Space": "2000 МБ облачного пространства для синхронизации",
|
||||
"Includes All Plus Tier Benefits": "Включает все преимущества тарифа Plus",
|
||||
"Unlimited AI Summaries": "Безлимитные AI-резюме",
|
||||
"Unlimited AI Translations": "Безлимитные AI-переводы",
|
||||
"Unlimited DeepL Pro Access": "Неограниченный доступ к DeepL Pro",
|
||||
"Advanced AI Tools": "Продвинутые AI-инструменты",
|
||||
"Early Feature Access": "Ранний доступ к новым функциям",
|
||||
"10 GB Cloud Sync Space": "10 ГБ облачного пространства для синхронизации",
|
||||
"Loading profile...": "Загрузка профиля...",
|
||||
"Features": "Функции",
|
||||
"Delete Account": "Удалить аккаунт",
|
||||
"Delete Your Account?": "Удалить ваш аккаунт?",
|
||||
"This action cannot be undone. All your data in the cloud will be permanently deleted.": "Это действие нельзя отменить. Все ваши данные в облаке будут удалены безвозвратно.",
|
||||
"Delete Permanently": "Удалить навсегда",
|
||||
"Free Tier": "Бесплатный тариф",
|
||||
"Plus Tier": "Тариф Plus",
|
||||
"Pro Tier": "Тариф Pro",
|
||||
"RTL Direction": "Направление RTL",
|
||||
"Maximum Column Height": "Максимальная высота колонки",
|
||||
"Maximum Column Width": "Максимальная ширина колонки",
|
||||
@@ -266,13 +236,13 @@
|
||||
"Clear Search": "Очистить поиск",
|
||||
"Header & Footer": "Заголовок и нижний колонтитул",
|
||||
"Apply also in Scrolled Mode": "Применить также в прокручиваемом режиме",
|
||||
"A new version of Readest is Available!": "Доступна новая версия Readest!",
|
||||
"A new version of Readest is available!": "Доступна новая версия Readest!",
|
||||
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} доступен (установленная версия {{currentVersion}}).",
|
||||
"Download and install now?": "Скачать и установить сейчас?",
|
||||
"Downloading {{downloaded}} of {{contentLength}}": "Скачано {{downloaded}} из {{contentLength}}",
|
||||
"Download finished": "Загрузка завершена",
|
||||
"DOWNLOAD & INSTALL": "СКАЧАТЬ И УСТАНОВИТЬ",
|
||||
"Changelog:": "Изменения:",
|
||||
"Changelog": "Изменения",
|
||||
"Software Update": "Обновление программного обеспечения",
|
||||
"Title": "Название",
|
||||
"Date Read": "Дата прочтения",
|
||||
@@ -281,10 +251,9 @@
|
||||
"Ascending": "По возрастанию",
|
||||
"Descending": "По убыванию",
|
||||
"Sort by...": "Сортировать по...",
|
||||
"Added:": "Добавлено:",
|
||||
"Format:": "Формат:",
|
||||
"Added": "Добавлено",
|
||||
"GuanKiapTsingKhai-T": "GuanKiapTsingKhai-T",
|
||||
"Description:": "Описание:",
|
||||
"Description": "Описание",
|
||||
"No description available": "Нет доступного описания",
|
||||
"List": "Список",
|
||||
"Grid": "Сетка",
|
||||
@@ -301,7 +270,7 @@
|
||||
"Checking for updates...": "Проверка обновлений...",
|
||||
"Error checking for updates": "Ошибка при проверке обновлений",
|
||||
"Details": "Инфо",
|
||||
"File Size:": "Размер файла:",
|
||||
"File Size": "Размер файла",
|
||||
"Auto Detect": "Автоопределение",
|
||||
"Next Section": "Следующий раздел",
|
||||
"Previous Section": "Предыдущий раздел",
|
||||
@@ -346,14 +315,12 @@
|
||||
"Synced at {{time}}": "Синхр. в {{time}}",
|
||||
"Never synced": "Не синхр.",
|
||||
"Show Remaining Time": "Показать оставшееся время",
|
||||
"Show Page Number": "Показать номер страницы",
|
||||
"{{time}} min left in chapter": "{{time}} мин осталось в главе",
|
||||
"Override Book Color": "Переопределить цвет книги",
|
||||
"Login Required": "Требуется вход",
|
||||
"Quota Exceeded": "Лимит превышен",
|
||||
"{{percentage}}% of Daily Translation Characters Used.": "Использовано {{percentage}}% от суточного лимита символов перевода.",
|
||||
"Translation Characters": "Символы перевода",
|
||||
"Daily translation quota reached. Select another translate service to proceed.": "Достигнут суточный лимит символов перевода. Выберите другой сервис перевода для продолжения.",
|
||||
"{{engine}}: {{count}} voices_one": "{{engine}}: {{count}} голос",
|
||||
"{{engine}}: {{count}} voices_few": "{{engine}}: {{count}} голоса",
|
||||
"{{engine}}: {{count}} voices_many": "{{engine}}: {{count}} голосов",
|
||||
@@ -365,5 +332,185 @@
|
||||
"Contact Support": "Связаться с поддержкой",
|
||||
"Code Highlighting": "Подсветка кода",
|
||||
"Enable Highlighting": "Включить подсветку",
|
||||
"Code Language": "Язык кода"
|
||||
"Code Language": "Язык кода",
|
||||
"Top Margin (px)": "Верхний отступ (пикс)",
|
||||
"Bottom Margin (px)": "Нижний отступ (пикс)",
|
||||
"Right Margin (px)": "Правый отступ (пикс)",
|
||||
"Left Margin (px)": "Левый отступ (пикс)",
|
||||
"Column Gap (%)": "Промежуток между колонками (%)",
|
||||
"Always Show Status Bar": "Всегда показывать панель состояния",
|
||||
"Translation Not Available": "Перевод недоступен",
|
||||
"Custom Content CSS": "CSS содержимого",
|
||||
"Enter CSS for book content styling...": "Введите CSS для оформления содержимого книги...",
|
||||
"Custom Reader UI CSS": "CSS интерфейса",
|
||||
"Enter CSS for reader interface styling...": "Введите CSS для оформления интерфейса ридера...",
|
||||
"Crop": "Обрезать",
|
||||
"Book Covers": "Обложки книг",
|
||||
"Fit": "Подогнать",
|
||||
"Reset {{settings}}": "Сбросить {{settings}}",
|
||||
"Reset Settings": "Сбросить настройки",
|
||||
"{{count}} pages left in chapter_one": "Осталась {{count}} страница в главе",
|
||||
"{{count}} pages left in chapter_few": "Осталось {{count}} страницы в главе",
|
||||
"{{count}} pages left in chapter_many": "Осталось {{count}} страниц в главе",
|
||||
"{{count}} pages left in chapter_other": "Осталось {{count}} страниц в главе",
|
||||
"Show Remaining Pages": "Показать оставшиеся страницы",
|
||||
"Source Han Serif CN VF": "Source Han Serif",
|
||||
"Huiwen-mincho": "Huiwen Mincho",
|
||||
"KingHwa_OldSong": "KingHwa Song",
|
||||
"Manage Subscription": "Управление подпиской",
|
||||
"Coming Soon": "Скоро будет",
|
||||
"Upgrade to {{plan}}": "Обновить до {{plan}}",
|
||||
"Upgrade to Plus or Pro": "Обновить до Plus или Pro",
|
||||
"Current Plan": "Текущий план",
|
||||
"Plan Limits": "Лимиты плана",
|
||||
"Processing your payment...": "Обработка платежа...",
|
||||
"Please wait while we confirm your subscription.": "Пожалуйста, подождите, пока мы подтверждаем вашу подписку.",
|
||||
"Payment Processing": "Обработка платежа",
|
||||
"Your payment is being processed. This usually takes a few moments.": "Ваш платеж обрабатывается. Обычно это занимает несколько секунд.",
|
||||
"Payment Failed": "Не удалось провести платеж",
|
||||
"We couldn't process your subscription. Please try again or contact support if the issue persists.": "Не удалось обработать подписку. Пожалуйста, попробуйте еще раз или свяжитесь с поддержкой, если проблема сохраняется.",
|
||||
"Back to Profile": "Назад в профиль",
|
||||
"Subscription Successful!": "Подписка успешно оформлена!",
|
||||
"Thank you for your subscription! Your payment has been processed successfully.": "Спасибо за подписку! Ваш платеж успешно обработан.",
|
||||
"Email:": "Электронная почта:",
|
||||
"Plan:": "План:",
|
||||
"Amount:": "Сумма:",
|
||||
"Subscription ID:": "ID подписки:",
|
||||
"Go to Library": "Перейти в библиотеку",
|
||||
"Need help? Contact our support team at support@readest.com": "Нужна помощь? Свяжитесь с нашей поддержкой: support@readest.com",
|
||||
"Free Plan": "Бесплатный план",
|
||||
"month": "месяц",
|
||||
"AI Translations (per day)": "Переводы ИИ (в день)",
|
||||
"Plus Plan": "План Plus",
|
||||
"Includes All Free Plan Benefits": "Включает все преимущества бесплатного плана",
|
||||
"Pro Plan": "План Pro",
|
||||
"More AI Translations": "Больше переводов ИИ",
|
||||
"Complete Your Subscription": "Завершите подписку",
|
||||
"{{percentage}}% of Cloud Sync Space Used.": "Использовано {{percentage}}% облачного пространства для синхронизации.",
|
||||
"Cloud Sync Storage": "Облачное хранилище",
|
||||
"Disable": "Отключить",
|
||||
"Enable": "Включить",
|
||||
"Upgrade to Readest Premium": "Обновить до Readest Premium",
|
||||
"Show Source Text": "Показать исходный текст",
|
||||
"Cross-Platform Sync": "Кроссплатформенная Синхронизация",
|
||||
"Seamlessly sync your library, progress, highlights, and notes across all your devices—never lose your place again.": "Беспрепятственно синхронизируйте свою библиотеку, прогресс, заметки и выделения на всех устройствах—больше никогда не теряйте свое место.",
|
||||
"Customizable Reading": "Настраиваемое Чтение",
|
||||
"Personalize every detail with adjustable fonts, layouts, themes, and advanced display settings for the perfect reading experience.": "Персонализируйте каждую деталь с настраиваемыми шрифтами, макетами, темами и расширенными настройками отображения для идеального чтения.",
|
||||
"AI Read Aloud": "Озвучивание ИИ",
|
||||
"Enjoy hands-free reading with natural-sounding AI voices that bring your books to life.": "Наслаждайтесь чтением без рук с естественно звучащими голосами ИИ, которые оживляют ваши книги.",
|
||||
"AI Translations": "Переводы ИИ",
|
||||
"Translate any text instantly with the power of Google, Azure, or DeepL—understand content in any language.": "Переводите любой текст мгновенно с помощью Google, Azure или DeepL—понимайте содержание на любом языке.",
|
||||
"Connect with fellow readers and get help fast in our friendly community channels.": "Общайтесь с другими читателями и получайте быструю помощь в наших дружелюбных каналах сообщества.",
|
||||
"Unlimited AI Read Aloud Hours": "Неограниченные Часы Озвучивания ИИ",
|
||||
"Listen without limits—convert as much text as you like into immersive audio.": "Слушайте без ограничений—преобразуйте столько текста, сколько хотите, в захватывающее аудио.",
|
||||
"Unlock enhanced translation capabilities with more daily usage and advanced options.": "Разблокируйте улучшенные возможности перевода с большим ежедневным использованием и расширенными опциями.",
|
||||
"DeepL Pro Access": "Доступ к DeepL Pro",
|
||||
"Enjoy faster responses and dedicated assistance whenever you need help.": "Наслаждайтесь более быстрыми ответами и персональной помощью, когда вам нужна поддержка.",
|
||||
"Daily translation quota reached. Upgrade your plan to continue using AI translations.": "Достигнута дневная квота переводов. Обновите план, чтобы продолжить использовать переводы ИИ.",
|
||||
"Includes All Plus Plan Benefits": "Включает Все Преимущества Plus Плана",
|
||||
"Early Feature Access": "Ранний Доступ к Функциям",
|
||||
"Be the first to explore new features, updates, and innovations before anyone else.": "Исследуйте новые функции, обновления и инновации первыми.",
|
||||
"Advanced AI Tools": "Продвинутые ИИ Инструменты",
|
||||
"Harness powerful AI tools for smarter reading, translation, and content discovery.": "Используйте мощные ИИ инструменты для умного чтения, перевода и поиска контента.",
|
||||
"Translate up to 100,000 characters daily with the most accurate translation engine available.": "Переводите до 100 000 символов ежедневно с самым точным доступным движком перевода.",
|
||||
"Translate up to 500,000 characters daily with the most accurate translation engine available.": "Переводите до 500 000 символов ежедневно с самым точным доступным движком перевода.",
|
||||
"Securely store and access your entire reading collection with up to 5 GB of cloud storage.": "Безопасно храните и получайте доступ ко всей коллекции чтения с до 5 ГБ облачного хранилища.",
|
||||
"Securely store and access your entire reading collection with up to 20 GB of cloud storage.": "Безопасно храните и получайте доступ ко всей коллекции чтения с до 20 ГБ облачного хранилища.",
|
||||
"Deleted cloud backup of the book: {{title}}": "Удалено облачное резервное копирование книги: {{title}}",
|
||||
"Are you sure to delete the cloud backup of the selected book?": "Вы уверены, что хотите удалить облачное резервное копирование выбранной книги?",
|
||||
"What's New in Readest": "Что нового в Readest",
|
||||
"Enter book title": "Введите название книги",
|
||||
"Subtitle": "Подзаголовок",
|
||||
"Enter book subtitle": "Введите подзаголовок книги",
|
||||
"Enter author name": "Введите имя автора",
|
||||
"Series": "Серия",
|
||||
"Enter series name": "Введите название серии",
|
||||
"Series Index": "Номер в серии",
|
||||
"Enter series index": "Введите номер в серии",
|
||||
"Total in Series": "Всего в серии",
|
||||
"Enter total books in series": "Введите общее количество книг в серии",
|
||||
"Enter publisher": "Введите издательство",
|
||||
"Publication Date": "Дата публикации",
|
||||
"Identifier": "Идентификатор",
|
||||
"Enter book description": "Введите описание книги",
|
||||
"Change cover image": "Изменить обложку",
|
||||
"Replace": "Заменить",
|
||||
"Unlock cover": "Разблокировать обложку",
|
||||
"Lock cover": "Заблокировать обложку",
|
||||
"Auto-Retrieve Metadata": "Автоматически получить метаданные",
|
||||
"Auto-Retrieve": "Автоматическое получение",
|
||||
"Unlock all fields": "Разблокировать все поля",
|
||||
"Unlock All": "Разблокировать всё",
|
||||
"Lock all fields": "Заблокировать все поля",
|
||||
"Lock All": "Заблокировать всё",
|
||||
"Reset": "Сбросить",
|
||||
"Edit Metadata": "Редактировать метаданные",
|
||||
"Locked": "Заблокировано",
|
||||
"Select Metadata Source": "Выберите источник метаданных",
|
||||
"Keep manual input": "Сохранить ручной ввод",
|
||||
"Google Books": "Google Книги",
|
||||
"Open Library": "Open Library",
|
||||
"Fiction, Science, History": "Художественная литература, Наука, История",
|
||||
"Select Cover Image": "Выбрать изображение обложки",
|
||||
"Open Book in New Window": "Открыть книгу в новом окне",
|
||||
"Voices for {{lang}}": "Голоса для {{lang}}",
|
||||
"Yandex Translate": "Yandex Translate",
|
||||
"YYYY or YYYY-MM-DD": "YYYY или YYYY-MM-DD",
|
||||
"Restore Purchase": "Восстановить покупку",
|
||||
"No purchases found to restore.": "Не найдено покупок для восстановления.",
|
||||
"Failed to restore purchases.": "Не удалось восстановить покупки.",
|
||||
"Failed to manage subscription.": "Не удалось управлять подпиской.",
|
||||
"Failed to load subscription plans.": "Не удалось загрузить планы подписки.",
|
||||
"year": "год",
|
||||
"Failed to create checkout session": "Не удалось создать сессию оформления заказа",
|
||||
"Storage": "Хранилище",
|
||||
"Terms of Service": "Условия использования",
|
||||
"Privacy Policy": "Политика конфиденциальности",
|
||||
"Disable Double Click": "Отключить двойной клик",
|
||||
"TTS not supported for this document": "TTS не поддерживается для этого документа",
|
||||
"Reset Password": "Сбросить пароль",
|
||||
"Show Reading Progress": "Показать прогресс чтения",
|
||||
"Reading Progress Style": "Стиль прогресса чтения",
|
||||
"Page Number": "Номер страницы",
|
||||
"Percentage": "Процент",
|
||||
"Deleted local copy of the book: {{title}}": "Удалена локальная копия книги: {{title}}",
|
||||
"Failed to delete cloud backup of the book: {{title}}": "Не удалось удалить резервную копию книги в облаке: {{title}}",
|
||||
"Failed to delete local copy of the book: {{title}}": "Не удалось удалить локальную копию книги: {{title}}",
|
||||
"Are you sure to delete the local copy of the selected book?": "Вы уверены, что хотите удалить локальную копию выбранной книги?",
|
||||
"Remove from Cloud & Device": "Удалить из облака и устройства",
|
||||
"Remove from Cloud Only": "Удалить только из облака",
|
||||
"Remove from Device Only": "Удалить только с устройства",
|
||||
"Disconnected": "Отключено",
|
||||
"KOReader Sync Settings": "Настройки синхронизации KOReader",
|
||||
"Sync Strategy": "Стратегия синхронизации",
|
||||
"Ask on conflict": "Спрашивать при конфликте",
|
||||
"Always use latest": "Всегда использовать последнюю",
|
||||
"Send changes only": "Отправлять только изменения",
|
||||
"Receive changes only": "Получать только изменения",
|
||||
"Disabled": "Отключено",
|
||||
"Checksum Method": "Метод контрольной суммы",
|
||||
"File Content (recommended)": "Содержимое файла (рекомендуется)",
|
||||
"File Name": "Имя файла",
|
||||
"Device Name": "Имя устройства",
|
||||
"Sync Tolerance": "Допуск синхронизации",
|
||||
"Connect to your KOReader Sync server.": "Подключитесь к вашему серверу синхронизации KOReader.",
|
||||
"Server URL": "URL сервера",
|
||||
"Username": "Имя пользователя",
|
||||
"Your Username": "Ваше имя пользователя",
|
||||
"Password": "Пароль",
|
||||
"Connect": "Подключиться",
|
||||
"KOReader Sync": "KOReader Sync",
|
||||
"Sync Conflict": "Конфликт синхронизации",
|
||||
"Sync reading progress from \"{{deviceName}}\"?": "Синхронизировать прогресс чтения с \"{{deviceName}}\"?",
|
||||
"another device": "другом устройстве",
|
||||
"Local Progress": "Локальный прогресс",
|
||||
"Remote Progress": "Удаленный прогресс",
|
||||
"Page {{page}} of {{total}} ({{percentage}}%)": "Страница {{page}} из {{total}} ({{percentage}}%)",
|
||||
"Current position": "Текущая позиция",
|
||||
"Approximately page {{page}} of {{total}} ({{percentage}}%)": "Приблизительно страница {{page}} из {{total}} ({{percentage}}%)",
|
||||
"Approximately {{percentage}}%": "Приблизительно {{percentage}}%",
|
||||
"Failed to connect": "Не удалось подключиться",
|
||||
"Sync Server Connected": "Сервер синхронизации подключен",
|
||||
"Precision: {{precision}} digits after the decimal": "Точность: {{precision}} знаков после запятой",
|
||||
"Sync as {{userDisplayName}}": "Синхронизировать как {{userDisplayName}}"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,504 @@
|
||||
{
|
||||
"Email address": "อีเมล",
|
||||
"Your Password": "รหัสผ่าน",
|
||||
"Your email address": "อีเมลของคุณ",
|
||||
"Your password": "รหัสผ่านของคุณ",
|
||||
"Sign in": "เข้าสู่ระบบ",
|
||||
"Signing in...": "กำลังเข้าสู่ระบบ...",
|
||||
"Sign in with {{provider}}": "เข้าสู่ระบบด้วย {{provider}}",
|
||||
"Already have an account? Sign in": "มีบัญชีแล้ว? เข้าสู่ระบบ",
|
||||
"Create a Password": "สร้างรหัสผ่าน",
|
||||
"Sign up": "สมัครสมาชิก",
|
||||
"Signing up...": "กำลังสมัครสมาชิก...",
|
||||
"Check your email for the confirmation link": "ตรวจสอบอีเมลเพื่อยืนยัน",
|
||||
"Signing in ...": "กำลังเข้าสู่ระบบ...",
|
||||
"Send a magic link email": "ส่งลิงก์เข้าสู่ระบบ",
|
||||
"Check your email for the magic link": "ตรวจสอบอีเมลเพื่อรับลิงก์",
|
||||
"Send reset password instructions": "ส่งคำแนะนำรีเซ็ตรหัส",
|
||||
"Sending reset instructions ...": "กำลังส่งคำแนะนำ...",
|
||||
"Forgot your password?": "ลืมรหัสผ่าน?",
|
||||
"Check your email for the password reset link": "ตรวจสอบอีเมลเพื่อรีเซ็ตรหัส",
|
||||
"Phone number": "เบอร์โทรศัพท์",
|
||||
"Your phone number": "เบอร์โทรศัพท์ของคุณ",
|
||||
"Token": "รหัสยืนยัน",
|
||||
"Your OTP token": "รหัส OTP ของคุณ",
|
||||
"Verify token": "ยืนยันรหัส",
|
||||
"Sign in with Google": "เข้าสู่ระบบด้วย Google",
|
||||
"Sign in with Apple": "เข้าสู่ระบบด้วย Apple",
|
||||
"Sign in with GitHub": "เข้าสู่ระบบด้วย GitHub",
|
||||
"New Password": "รหัสผ่านใหม่",
|
||||
"Your new password": "รหัสผ่านใหม่ของคุณ",
|
||||
"Update password": "อัปเดตรหัสผ่าน",
|
||||
"Updating password ...": "กำลังอัปเดตรหัสผ่าน...",
|
||||
"Your password has been updated": "รหัสผ่านได้รับการอัปเดตแล้ว",
|
||||
"Something went wrong. Don't worry, our team has been notified and we're working on a fix.": "เกิดข้อผิดพลาด ทีมงานได้รับแจ้งแล้วและกำลังแก้ไข",
|
||||
"Error Details:": "รายละเอียดข้อผิดพลาด:",
|
||||
"Try Again": "ลองใหม่",
|
||||
"Go Back": "กลับ",
|
||||
"Your Library": "คลังหนังสือ",
|
||||
"Need help?": "ต้องการความช่วยเหลือ?",
|
||||
"Contact Support": "ติดต่อฝ่ายสนับสนุน",
|
||||
"Open": "เปิด",
|
||||
"Group": "กลุ่ม",
|
||||
"Details": "รายละเอียด",
|
||||
"Delete": "ลบ",
|
||||
"Cancel": "ยกเลิก",
|
||||
"Confirm Deletion": "ยืนยันการลบ",
|
||||
"Are you sure to delete {{count}} selected book(s)?_other": "ยืนยันลบหนังสือ {{count}} เล่ม?",
|
||||
"Deselect Book": "ยกเลิกเลือกหนังสือ",
|
||||
"Select Book": "เลือกหนังสือ",
|
||||
"Show Book Details": "แสดงรายละเอียดหนังสือ",
|
||||
"Download Book": "ดาวน์โหลดหนังสือ",
|
||||
"Upload Book": "อัปโหลดหนังสือ",
|
||||
"Deselect Group": "ยกเลิกเลือกกลุ่ม",
|
||||
"Select Group": "เลือกกลุ่ม",
|
||||
"Untitled Group": "กลุ่มไม่มีชื่อ",
|
||||
"Group Books": "จัดกลุ่มหนังสือ",
|
||||
"Remove From Group": "ลบออกจากกลุ่ม",
|
||||
"Create New Group": "สร้างกลุ่มใหม่",
|
||||
"Save": "บันทึก",
|
||||
"Confirm": "ยืนยัน",
|
||||
"From Local File": "จากไฟล์ในเครื่อง",
|
||||
"Search in {{count}} Book(s)..._other": "ค้นหาใน {{count}} เล่ม...",
|
||||
"Search Books...": "ค้นหาหนังสือ...",
|
||||
"Clear Search": "ล้างการค้นหา",
|
||||
"Import Books": "นำเข้าหนังสือ",
|
||||
"Select Multiple Books": "เลือกหลายเล่ม",
|
||||
"Select Books": "เลือกหนังสือ",
|
||||
"Deselect": "ยกเลิกเลือก",
|
||||
"Select All": "เลือกทั้งหมด",
|
||||
"Logged in as {{userDisplayName}}": "เข้าสู่ระบบในชื่อ {{userDisplayName}}",
|
||||
"Logged in": "เข้าสู่ระบบแล้ว",
|
||||
"Account": "บัญชี",
|
||||
"Sign In": "เข้าสู่ระบบ",
|
||||
"Auto Upload Books to Cloud": "อัปโหลดหนังสืออัตโนมัติ",
|
||||
"Auto Import on File Open": "นำเข้าอัตโนมัติเมื่อเปิดไฟล์",
|
||||
"Open Last Book on Start": "เปิดหนังสือล่าสุดเมื่อเริ่มใช้",
|
||||
"Check Updates on Start": "ตรวจสอบอัปเดตเมื่อเริ่มใช้",
|
||||
"Fullscreen": "เต็มจอ",
|
||||
"Always on Top": "แสดงด้านบนเสมอ",
|
||||
"Always Show Status Bar": "แสดงแถบสถานะเสมอ",
|
||||
"Keep Screen Awake": "เปิดหน้าจอค้าง",
|
||||
"Reload Page": "โหลดหน้าใหม่",
|
||||
"Dark Mode": "โหมดมืด",
|
||||
"Light Mode": "โหมดสว่าง",
|
||||
"Auto Mode": "โหมดอัตโนมัติ",
|
||||
"Upgrade to Readest Premium": "อัปเกรดเป็น Readest Premium",
|
||||
"Download Readest": "ดาวน์โหลด Readest",
|
||||
"About Readest": "เกี่ยวกับ Readest",
|
||||
"Help improve Readest": "ช่วยพัฒนา Readest",
|
||||
"Sharing anonymized statistics": "แชร์สถิติแบบไม่ระบุตัวตน",
|
||||
"List": "รายการ",
|
||||
"Grid": "ตาราง",
|
||||
"Crop": "ครอบตัด",
|
||||
"Fit": "ปรับพอดี",
|
||||
"Title": "ชื่อเรื่อง",
|
||||
"Author": "ผู้แต่ง",
|
||||
"Format": "รูปแบบ",
|
||||
"Date Read": "วันที่อ่าน",
|
||||
"Date Added": "วันที่เพิ่ม",
|
||||
"Ascending": "น้อยไปมาก",
|
||||
"Descending": "มากไปน้อย",
|
||||
"Book Covers": "ปกหนังสือ",
|
||||
"Sort by...": "เรียงตาม...",
|
||||
"No supported files found. Supported formats: {{formats}}": "ไม่พบไฟล์ที่รองรับ รูปแบบที่รองรับ: {{formats}}",
|
||||
"No chapters detected.": "ไม่พบบท",
|
||||
"Failed to parse the EPUB file.": "ไม่สามารถอ่านไฟล์ EPUB",
|
||||
"This book format is not supported.": "ไม่รองรับรูปแบบหนังสือนี้",
|
||||
"Failed to import book(s): {{filenames}}": "ไม่สามารถนำเข้าหนังสือ: {{filenames}}",
|
||||
"Book uploaded: {{title}}": "อัปโหลดหนังสือแล้ว: {{title}}",
|
||||
"Insufficient storage quota": "พื้นที่จัดเก็บไม่เพียงพอ",
|
||||
"Failed to upload book: {{title}}": "ไม่สามารถอัปโหลดหนังสือ: {{title}}",
|
||||
"Book downloaded: {{title}}": "ดาวน์โหลดหนังสือแล้ว: {{title}}",
|
||||
"Failed to download book: {{title}}": "ไม่สามารถดาวน์โหลดหนังสือ: {{title}}",
|
||||
"Book deleted: {{title}}": "ลบหนังสือแล้ว: {{title}}",
|
||||
"Failed to delete book: {{title}}": "ไม่สามารถลบหนังสือ: {{title}}",
|
||||
"Deleted cloud backup of the book: {{title}}": "ลบสำรองบนคลาวด์แล้ว: {{title}}",
|
||||
"Welcome to your library. You can import your books here and read them anytime.": "ยินดีต้อนรับสู่คลังหนังสือ คุณสามารถนำเข้าหนังสือและอ่านได้ตลอดเวลา",
|
||||
"Copied to notebook": "คัดลอกไปยังสมุดบันทึก",
|
||||
"No annotations to export": "ไม่มีบันทึกให้ส่งออก",
|
||||
"Exported from Readest": "ส่งออกจาก Readest",
|
||||
"Highlights & Annotations": "ไฮไลต์และบันทึก",
|
||||
"Untitled": "ไม่มีชื่อ",
|
||||
"Note": "บันทึก",
|
||||
"Copied to clipboard": "คัดลอกแล้ว",
|
||||
"Copy": "คัดลอก",
|
||||
"Delete Highlight": "ลบไฮไลต์",
|
||||
"Highlight": "ไฮไลต์",
|
||||
"Annotate": "บันทึก",
|
||||
"Search": "ค้นหา",
|
||||
"Dictionary": "พจนานุกรม",
|
||||
"Wikipedia": "วิกิพีเดีย",
|
||||
"Translate": "แปล",
|
||||
"Speak": "อ่านออกเสียง",
|
||||
"Login Required": "ต้องเข้าสู่ระบบ",
|
||||
"Quota Exceeded": "เกินโควตา",
|
||||
"Unable to fetch the translation. Please log in first and try again.": "ไม่สามารถแปลได้ กรุณาเข้าสู่ระบบก่อน",
|
||||
"Unable to fetch the translation. Try again later.": "ไม่สามารถแปลได้ ลองใหม่ภายหลัง",
|
||||
"Original Text": "ข้อความต้นฉบับ",
|
||||
"Auto Detect": "ตรวจสอบอัตโนมัติ",
|
||||
"(detected)": "(ตรวจพบ)",
|
||||
"Translated Text": "ข้อความแปล",
|
||||
"Loading...": "กำลังโหลด...",
|
||||
"No translation available.": "ไม่มีการแปล",
|
||||
"Translated by {{provider}}.": "แปลโดย {{provider}}",
|
||||
"Bookmark": "บุ๊กมาร์ก",
|
||||
"Next Section": "ส่วนถัดไป",
|
||||
"Previous Section": "ส่วนก่อนหน้า",
|
||||
"Next Page": "หน้าถัดไป",
|
||||
"Previous Page": "หน้าก่อนหน้า",
|
||||
"Go Forward": "ไปข้างหน้า",
|
||||
"Small": "เล็ก",
|
||||
"Large": "ใหญ่",
|
||||
"Notebook": "สมุดบันทึก",
|
||||
"No notes match your search": "ไม่พบบันทึกที่ตรงกับการค้นหา",
|
||||
"Excerpts": "ข้อความที่เลือก",
|
||||
"Notes": "บันทึก",
|
||||
"Add your notes here...": "เพิ่มบันทึกที่นี่...",
|
||||
"Search notes and excerpts...": "ค้นหาบันทึกและข้อความ...",
|
||||
"{{time}} min left in chapter": "เหลือ {{time}} นาทีในบท",
|
||||
"{{count}} pages left in chapter_other": "เหลือ {{count}} หน้าในบท",
|
||||
"Theme Mode": "โหมดธีม",
|
||||
"Invert Image In Dark Mode": "กลับสีรูปในโหมดมืด",
|
||||
"Override Book Color": "เขียนทับสีหนังสือ",
|
||||
"Theme Color": "สีธีม",
|
||||
"Custom": "กำหนดเอง",
|
||||
"Code Highlighting": "ไฮไลต์โค้ด",
|
||||
"Enable Highlighting": "เปิดไฮไลต์",
|
||||
"Code Language": "ภาษาโค้ด",
|
||||
"Scroll": "เลื่อน",
|
||||
"Scrolled Mode": "โหมดเลื่อน",
|
||||
"Continuous Scroll": "เลื่อนต่อเนื่อง",
|
||||
"Overlap Pixels": "พิกเซลซ้อน",
|
||||
"Click": "คลิก",
|
||||
"Clicks for Page Flip": "คลิกเพื่อพลิกหน้า",
|
||||
"Swap Clicks Area": "สลับพื้นที่คลิก",
|
||||
"Volume Keys for Page Flip": "ปุ่มเสียงเพื่อพลิกหน้า",
|
||||
"Animation": "ภาพเคลื่อนไหว",
|
||||
"Paging Animation": "ภาพเคลื่อนไหวพลิกหน้า",
|
||||
"Security": "ความปลอดภัย",
|
||||
"Allow JavaScript": "อนุญาต JavaScript",
|
||||
"Enable only if you trust the file.": "เปิดเฉพาะไฟล์ที่เชื่อถือได้",
|
||||
"Global Settings": "การตั้งค่าทั่วไป",
|
||||
"Apply to All Books": "ใช้กับหนังสือทั้งหมด",
|
||||
"Apply to This Book": "ใช้กับหนังสือนี้",
|
||||
"Reset Settings": "รีเซ็ตการตั้งค่า",
|
||||
"System Fonts": "ฟอนต์ระบบ",
|
||||
"Serif Font": "ฟอนต์ Serif",
|
||||
"Sans-Serif Font": "ฟอนต์ Sans-Serif",
|
||||
"Override Book Font": "เขียนทับฟอนต์หนังสือ",
|
||||
"Font Size": "ขนาดฟอนต์",
|
||||
"Default Font Size": "ขนาดฟอนต์เริ่มต้น",
|
||||
"Minimum Font Size": "ขนาดฟอนต์ต่ำสุด",
|
||||
"Font Weight": "น้ำหนักฟอนต์",
|
||||
"Font Family": "ตระกูลฟอนต์",
|
||||
"Default Font": "ฟอนต์เริ่มต้น",
|
||||
"CJK Font": "ฟอนต์ CJK",
|
||||
"Font Face": "หน้าฟอนต์",
|
||||
"Monospace Font": "ฟอนต์ Monospace",
|
||||
"Auto": "อัตโนมัติ",
|
||||
"System Language": "ภาษาระบบ",
|
||||
"Language": "ภาษา",
|
||||
"Interface Language": "ภาษาส่วนติดต่อ",
|
||||
"Translation": "การแปล",
|
||||
"Enable Translation": "เปิดการแปล",
|
||||
"Show Source Text": "แสดงข้อความต้นฉบับ",
|
||||
"Translation Service": "บริการแปล",
|
||||
"Translate To": "แปลเป็น",
|
||||
"Override Book Layout": "เขียนทับเลย์เอาต์หนังสือ",
|
||||
"Writing Mode": "โหมดการเขียน",
|
||||
"Default": "เริ่มต้น",
|
||||
"Horizontal Direction": "ทิศทางแนวนอน",
|
||||
"Vertical Direction": "ทิศทางแนวตั้ง",
|
||||
"RTL Direction": "ทิศทาง RTL",
|
||||
"Border Frame": "กรอบขอบ",
|
||||
"Double Border": "ขอบคู่",
|
||||
"Border Color": "สีขอบ",
|
||||
"Paragraph": "ย่อหน้า",
|
||||
"Paragraph Margin": "ระยะขอบย่อหน้า",
|
||||
"Line Spacing": "ระยะห่างบรรทัด",
|
||||
"Word Spacing": "ระยะห่างคำ",
|
||||
"Letter Spacing": "ระยะห่างตัวอักษร",
|
||||
"Text Indent": "การเยื้องข้อความ",
|
||||
"Full Justification": "จัดแนวเต็มบรรทัด",
|
||||
"Hyphenation": "การใส่เครื่องหมายยัติภังค์",
|
||||
"Page": "หน้า",
|
||||
"Top Margin (px)": "ขอบบน (px)",
|
||||
"Bottom Margin (px)": "ขอบล่าง (px)",
|
||||
"Left Margin (px)": "ขอบซ้าย (px)",
|
||||
"Right Margin (px)": "ขอบขวา (px)",
|
||||
"Column Gap (%)": "ช่องว่างคอลัมน์ (%)",
|
||||
"Maximum Number of Columns": "จำนวนคอลัมน์สูงสุด",
|
||||
"Maximum Column Height": "ความสูงคอลัมน์สูงสุด",
|
||||
"Maximum Column Width": "ความกว้างคอลัมน์สูงสุด",
|
||||
"Header & Footer": "หัวกระดาษและท้ายกระดาษ",
|
||||
"Show Header": "แสดงหัวกระดาษ",
|
||||
"Show Footer": "แสดงท้ายกระดาษ",
|
||||
"Show Remaining Time": "แสดงเวลาที่เหลือ",
|
||||
"Show Remaining Pages": "แสดงหน้าที่เหลือ",
|
||||
"Apply also in Scrolled Mode": "ใช้ในโหมดเลื่อนด้วย",
|
||||
"Screen": "หน้าจอ",
|
||||
"Orientation": "การวางแนว",
|
||||
"Portrait": "แนวตั้ง",
|
||||
"Landscape": "แนวนอน",
|
||||
"Apply": "ใช้",
|
||||
"Custom Content CSS": "CSS เนื้อหากำหนดเอง",
|
||||
"Enter CSS for book content styling...": "ป้อน CSS สำหรับจัดรูปแบบเนื้อหา...",
|
||||
"Custom Reader UI CSS": "CSS ส่วนติดต่อผู้ใช้",
|
||||
"Enter CSS for reader interface styling...": "ป้อน CSS สำหรับจัดรูปแบบส่วนติดต่อ...",
|
||||
"Font": "ฟอนต์",
|
||||
"Layout": "เลย์เอาต์",
|
||||
"Color": "สี",
|
||||
"Behavior": "พฤติกรรม",
|
||||
"Reset {{settings}}": "รีเซ็ต {{settings}}",
|
||||
"(from 'As You Like It', Act II)": "(จาก 'As You Like It', องก์ที่ 2)",
|
||||
"Custom Theme": "ธีมกำหนดเอง",
|
||||
"Theme Name": "ชื่อธีม",
|
||||
"Text Color": "สีข้อความ",
|
||||
"Background Color": "สีพื้นหลัง",
|
||||
"Link Color": "สีลิงก์",
|
||||
"Preview": "ดูตัวอย่าง",
|
||||
"Font & Layout": "ฟอนต์และเลย์เอาต์",
|
||||
"Book Cover": "ปกหนังสือ",
|
||||
"More Info": "ข้อมูลเพิ่มเติม",
|
||||
"Parallel Read": "อ่านคู่ขนาน",
|
||||
"Disable": "ปิด",
|
||||
"Enable": "เปิด",
|
||||
"Export Annotations": "ส่งออกบันทึก",
|
||||
"Sort TOC by Page": "เรียงสารบัญตามหน้า",
|
||||
"Edit": "แก้ไข",
|
||||
"Search...": "ค้นหา...",
|
||||
"Book": "หนังสือ",
|
||||
"Chapter": "บท",
|
||||
"Match Case": "ตรงตามตัวอักษร",
|
||||
"Match Whole Words": "ตรงทั้งคำ",
|
||||
"Match Diacritics": "ตรงตามเครื่องหมายวรรณยุกต์",
|
||||
"TOC": "สารบัญ",
|
||||
"Sidebar": "แถบข้าง",
|
||||
"Disable Translation": "ปิดการแปล",
|
||||
"Translation Not Available": "ไม่มีการแปล",
|
||||
"TTS not supported for PDF": "ไม่รองรับ TTS สำหรับ PDF",
|
||||
"No Timeout": "ไม่จำกัดเวลา",
|
||||
"{{value}} minute": "{{value}} นาที",
|
||||
"{{value}} minutes": "{{value}} นาที",
|
||||
"{{value}} hour": "{{value}} ชั่วโมง",
|
||||
"{{value}} hours": "{{value}} ชั่วโมง",
|
||||
"Slow": "ช้า",
|
||||
"Fast": "เร็ว",
|
||||
"{{engine}}: {{count}} voices_other": "{{engine}}: {{count}} เสียง",
|
||||
"Sign in to Sync": "เข้าสู่ระบบเพื่อซิงค์",
|
||||
"Synced at {{time}}": "ซิงค์เมื่อ {{time}}",
|
||||
"Never synced": "ไม่เคยซิงค์",
|
||||
"Reading Progress Synced": "ซิงค์ความคืบหน้าแล้ว",
|
||||
"Delete Your Account?": "ลบบัญชีของคุณ?",
|
||||
"This action cannot be undone. All your data in the cloud will be permanently deleted.": "การกระทำนี้ไม่สามารถยกเลิกได้ ข้อมูลทั้งหมดบนคลาวด์จะถูกลบถาวร",
|
||||
"Delete Permanently": "ลบถาวร",
|
||||
"Manage Subscription": "จัดการการสมัครสมาชิก",
|
||||
"Sign Out": "ออกจากระบบ",
|
||||
"Delete Account": "ลบบัญชี",
|
||||
"Upgrade to {{plan}}": "อัปเกรดเป็น {{plan}}",
|
||||
"Complete Your Subscription": "ดำเนินการสมัครสมาชิกให้เสร็จ",
|
||||
"Coming Soon": "เร็วๆ นี้",
|
||||
"Upgrade to Plus or Pro": "อัปเกรดเป็น Plus หรือ Pro",
|
||||
"Current Plan": "แผนปัจจุบัน",
|
||||
"Plan Limits": "ข้อจำกัดแผน",
|
||||
"Failed to delete user. Please try again later.": "ไม่สามารถลบผู้ใช้ ลองใหม่ภายหลัง",
|
||||
"Loading profile...": "กำลังโหลดโปรไฟล์...",
|
||||
"Processing your payment...": "กำลังประมวลผลการชำระเงิน...",
|
||||
"Please wait while we confirm your subscription.": "กรุณารอสักครู่เพื่อยืนยันการสมัครสมาชิก",
|
||||
"Payment Processing": "กำลังประมวลผลการชำระเงิน",
|
||||
"Your payment is being processed. This usually takes a few moments.": "กำลังประมวลผลการชำระเงิน โดยปกติใช้เวลาสักครู่",
|
||||
"Payment Failed": "การชำระเงินล้มเหลว",
|
||||
"We couldn't process your subscription. Please try again or contact support if the issue persists.": "ไม่สามารถประมวลผลการสมัครสมาชิก ลองใหม่หรือติดต่อฝ่ายสนับสนุน",
|
||||
"Back to Profile": "กลับไปที่โปรไฟล์",
|
||||
"Subscription Successful!": "สมัครสมาชิกสำเร็จ!",
|
||||
"Thank you for your subscription! Your payment has been processed successfully.": "ขอบคุณสำหรับการสมัครสมาชิก! ชำระเงินเรียบร้อยแล้ว",
|
||||
"Email:": "อีเมล:",
|
||||
"Plan:": "แผน:",
|
||||
"Amount:": "จำนวนเงิน:",
|
||||
"Subscription ID:": "รหัสสมาชิก:",
|
||||
"Go to Library": "ไปที่คลังหนังสือ",
|
||||
"Need help? Contact our support team at support@readest.com": "ต้องการความช่วยเหลือ? ติดต่อทีมสนับสนุนที่ support@readest.com",
|
||||
"Free Plan": "แผนฟรี",
|
||||
"month": "เดือน",
|
||||
"Cross-Platform Sync": "ซิงค์ข้ามแพลตฟอร์ม",
|
||||
"Seamlessly sync your library, progress, highlights, and notes across all your devices—never lose your place again.": "ซิงค์คลังหนังสือ ความคืบหน้า ไฮไลต์ และบันทึกข้ามอุปกรณ์—ไม่สูญหายอีกต่อไป",
|
||||
"Customizable Reading": "การอ่านแบบปรับแต่งได้",
|
||||
"Personalize every detail with adjustable fonts, layouts, themes, and advanced display settings for the perfect reading experience.": "ปรับแต่งทุกรายละเอียดด้วยฟอนต์ เลย์เอาต์ ธีม และการตั้งค่าขั้นสูงเพื่อประสบการณ์การอ่านที่สมบูรณ์แบบ",
|
||||
"AI Read Aloud": "AI อ่านออกเสียง",
|
||||
"Enjoy hands-free reading with natural-sounding AI voices that bring your books to life.": "เพลิดเพลินกับการอ่านแบบเสรีมือด้วยเสียง AI ที่เป็นธรรมชาติ",
|
||||
"AI Translations": "การแปลด้วย AI",
|
||||
"Translate any text instantly with the power of Google, Azure, or DeepL—understand content in any language.": "แปลข้อความได้ทันทีด้วยพลัง Google, Azure หรือ DeepL—เข้าใจเนื้อหาทุกภาษา",
|
||||
"Community Support": "การสนับสนุนชุมชน",
|
||||
"Connect with fellow readers and get help fast in our friendly community channels.": "เชื่อมต่อกับนักอ่านอื่นๆ และรับความช่วยเหลือในชุมชนที่เป็นมิตร",
|
||||
"Cloud Sync Storage": "พื้นที่จัดเก็บซิงค์คลาวด์",
|
||||
"AI Translations (per day)": "การแปล AI (ต่อวัน)",
|
||||
"Plus Plan": "แผน Plus",
|
||||
"Includes All Free Plan Benefits": "รวมสิทธิประโยชน์แผนฟรีทั้งหมด",
|
||||
"Unlimited AI Read Aloud Hours": "AI อ่านออกเสียงไม่จำกัด",
|
||||
"Listen without limits—convert as much text as you like into immersive audio.": "ฟังไม่จำกัด—แปลงข้อความเป็นเสียงได้ไม่อั้น",
|
||||
"More AI Translations": "การแปล AI เพิ่มขึ้น",
|
||||
"Unlock enhanced translation capabilities with more daily usage and advanced options.": "ปลดล็อกความสามารถการแปลที่เพิ่มขึ้นพร้อมโควตารายวันและตัวเลือกขั้นสูง",
|
||||
"DeepL Pro Access": "การเข้าถึง DeepL Pro",
|
||||
"Translate up to 100,000 characters daily with the most accurate translation engine available.": "แปลได้สูงสุด 100,000 อักขระต่อวันด้วยเครื่องมือแปลที่แม่นยำที่สุด",
|
||||
"Securely store and access your entire reading collection with up to 5 GB of cloud storage.": "จัดเก็บและเข้าถึงคอลเลคชันการอ่านอย่างปลอดภัยด้วยพื้นที่คลาวด์ 5 GB",
|
||||
"Priority Support": "การสนับสนุนแบบพิเศษ",
|
||||
"Enjoy faster responses and dedicated assistance whenever you need help.": "รับการตอบสนองที่รวดเร็วและความช่วยเหลือเฉพาะเมื่อต้องการ",
|
||||
"Pro Plan": "แผน Pro",
|
||||
"Includes All Plus Plan Benefits": "รวมสิทธิประโยชน์แผน Plus ทั้งหมด",
|
||||
"Early Feature Access": "เข้าถึงฟีเจอร์ใหม่ก่อนใคร",
|
||||
"Be the first to explore new features, updates, and innovations before anyone else.": "เป็นคนแรกที่ได้สำรวจฟีเจอร์ใหม่ อัปเดต และนวัตกรรมก่อนใคร",
|
||||
"Advanced AI Tools": "เครื่องมือ AI ขั้นสูง",
|
||||
"Harness powerful AI tools for smarter reading, translation, and content discovery.": "ใช้เครื่องมือ AI อันทรงพลังเพื่อการอ่าน การแปล และการค้นหาเนื้อหาที่ฉลาดขึ้น",
|
||||
"Translate up to 500,000 characters daily with the most accurate translation engine available.": "แปลได้สูงสุด 500,000 อักขระต่อวันด้วยเครื่องมือแปลที่แม่นยำที่สุด",
|
||||
"Securely store and access your entire reading collection with up to 20 GB of cloud storage.": "จัดเก็บและเข้าถึงคอลเลคชันการอ่านอย่างปลอดภัยด้วยพื้นที่คลาวด์ 20 GB",
|
||||
"Version {{version}}": "เวอร์ชัน {{version}}",
|
||||
"Check Update": "ตรวจสอบอัปเดต",
|
||||
"Already the latest version": "เป็นเวอร์ชันล่าสุดแล้ว",
|
||||
"Checking for updates...": "กำลังตรวจสอบอัปเดต...",
|
||||
"Error checking for updates": "เกิดข้อผิดพลาดในการตรวจสอบอัปเดต",
|
||||
"Book Details": "รายละเอียดหนังสือ",
|
||||
"Unknown": "ไม่ทราบ",
|
||||
"Publisher": "สำนักพิมพ์",
|
||||
"Published": "วันที่พิมพ์",
|
||||
"Updated": "อัปเดต",
|
||||
"Added": "เพิ่มเมื่อ",
|
||||
"Subjects": "หัวข้อ",
|
||||
"File Size": "ขนาดไฟล์",
|
||||
"Description": "คำอธิบาย",
|
||||
"No description available": "ไม่มีคำอธิบาย",
|
||||
"Are you sure to delete the selected book?": "ยืนยันลบหนังสือที่เลือก?",
|
||||
"Are you sure to delete the cloud backup of the selected book?": "ยืนยันลบสำรองคลาวด์ของหนังสือที่เลือก?",
|
||||
"Drop to Import Books": "วางเพื่อนำเข้าหนังสือ",
|
||||
"A new version of Readest is available!": "Readest เวอร์ชันใหม่พร้อมใช้งาน!",
|
||||
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} พร้อมใช้งาน (เวอร์ชันที่ติดตั้ง {{currentVersion}})",
|
||||
"Download and install now?": "ดาวน์โหลดและติดตั้งเลย?",
|
||||
"Downloading {{downloaded}} of {{contentLength}}": "กำลังดาวน์โหลด {{downloaded}} จาก {{contentLength}}",
|
||||
"Download finished": "ดาวน์โหลดเสร็จสิ้น",
|
||||
"DOWNLOAD & INSTALL": "ดาวน์โหลดและติดตั้ง",
|
||||
"Changelog": "รายการเปลี่ยนแปลง",
|
||||
"Software Update": "อัปเดตซอฟต์แวร์",
|
||||
"{{percentage}}% of Cloud Sync Space Used.": "ใช้พื้นที่ซิงค์คลาวด์ {{percentage}}%",
|
||||
"Translation Characters": "อักขระการแปล",
|
||||
"{{percentage}}% of Daily Translation Characters Used.": "ใช้อักขระการแปลรายวัน {{percentage}}%",
|
||||
"Daily translation quota reached. Upgrade your plan to continue using AI translations.": "ใช้โควตาการแปลรายวันครบแล้ว อัปเกรดแผนเพื่อใช้การแปล AI ต่อ",
|
||||
"LXGW WenKai GB Screen": "LXGW WenKai GB Screen",
|
||||
"LXGW WenKai TC": "LXGW WenKai TC",
|
||||
"GuanKiapTsingKhai-T": "GuanKiapTsingKhai-T",
|
||||
"Source Han Serif CN VF": "Source Han Serif CN VF",
|
||||
"Huiwen-mincho": "Huiwen-mincho",
|
||||
"KingHwa_OldSong": "KingHwa_OldSong",
|
||||
"Azure Translator": "Azure Translator",
|
||||
"DeepL": "DeepL",
|
||||
"Google Translate": "Google Translate",
|
||||
"Gray": "สีเทา",
|
||||
"Sepia": "สีซีเปีย",
|
||||
"Grass": "สีเขียว",
|
||||
"Cherry": "สีชมพู",
|
||||
"Sky": "สีฟ้า",
|
||||
"Solarized": "Solarized",
|
||||
"Gruvbox": "Gruvbox",
|
||||
"Nord": "Nord",
|
||||
"Contrast": "ความคมชัด",
|
||||
"Sunset": "สีพระอาทิตย์ตก",
|
||||
"Reveal in Finder": "แสดงใน Finder",
|
||||
"Reveal in File Explorer": "แสดงใน File Explorer",
|
||||
"Reveal in Folder": "แสดงในโฟลเดอร์",
|
||||
"Don't have an account? Sign up": "ยังไม่มีบัญชี? สมัครสมาชิก",
|
||||
"All the world's a stage,\nAnd all the men and women merely players;\nThey have their exits and their entrances,\nAnd one man in his time plays many parts,\nHis acts being seven ages.\n\n— William Shakespeare": "โลกทั้งใบเป็นเวที\nและชายหญิงทั้งปวงเป็นนักแสดง\nมีทางเข้าและทางออก\nแต่ละคนในชีวิตเล่นหลายบท\nการกระทำของเขาแบ่งเป็นเจ็ดวัย\n\n— วิลเลียม เชกสเปียร์",
|
||||
"What's New in Readest": "มีอะไรใหม่ใน Readest",
|
||||
"Enter book title": "ป้อนชื่อหนังสือ",
|
||||
"Subtitle": "หัวข้อรอง",
|
||||
"Enter book subtitle": "ป้อนหัวข้อรองของหนังสือ",
|
||||
"Enter author name": "ป้อนชื่อผู้แต่ง",
|
||||
"Series": "ชุด",
|
||||
"Enter series name": "ป้อนชื่อชุด",
|
||||
"Series Index": "เลขที่ในชุด",
|
||||
"Enter series index": "ป้อนเลขที่ในชุด",
|
||||
"Total in Series": "จำนวนรวมในชุด",
|
||||
"Enter total books in series": "ป้อนจำนวนหนังสือทั้งหมดในชุด",
|
||||
"Enter publisher": "ป้อนสำนักพิมพ์",
|
||||
"Publication Date": "วันที่พิมพ์",
|
||||
"Identifier": "ตัวระบุ",
|
||||
"Enter book description": "ป้อนคำอธิบายหนังสือ",
|
||||
"Change cover image": "เปลี่ยนรูปปก",
|
||||
"Replace": "แทนที่",
|
||||
"Unlock cover": "ปลดล็อคปก",
|
||||
"Lock cover": "ล็อคปก",
|
||||
"Auto-Retrieve Metadata": "ดึงข้อมูลเมตาอัตโนมัติ",
|
||||
"Auto-Retrieve": "ดึงข้อมูลอัตโนมัติ",
|
||||
"Unlock all fields": "ปลดล็อคทุกช่อง",
|
||||
"Unlock All": "ปลดล็อคทั้งหมด",
|
||||
"Lock all fields": "ล็อคทุกช่อง",
|
||||
"Lock All": "ล็อคทั้งหมด",
|
||||
"Reset": "รีเซ็ต",
|
||||
"Edit Metadata": "แก้ไขข้อมูลเมตา",
|
||||
"Locked": "ล็อคแล้ว",
|
||||
"Select Metadata Source": "เลือกแหล่งข้อมูลเมตา",
|
||||
"Keep manual input": "เก็บข้อมูลที่ป้อนด้วยตนเอง",
|
||||
"Google Books": "Google Books",
|
||||
"Open Library": "Open Library",
|
||||
"Fiction, Science, History": "นิยาย, วิทยาศาสตร์, ประวัติศาสตร์",
|
||||
"Select Cover Image": "เลือกภาพปก",
|
||||
"Open Book in New Window": "เปิดหนังสือในหน้าต่างใหม่",
|
||||
"Voices for {{lang}}": "เสียงสำหรับ {{lang}}",
|
||||
"Yandex Translate": "Yandex Translate",
|
||||
"YYYY or YYYY-MM-DD": "YYYY หรือ YYYY-MM-DD",
|
||||
"Restore Purchase": "กู้คืนการซื้อ",
|
||||
"No purchases found to restore.": "ไม่พบการซื้อเพื่อกู้คืน",
|
||||
"Failed to restore purchases.": "ไม่สามารถกู้คืนการซื้อได้",
|
||||
"Failed to manage subscription.": "ไม่สามารถจัดการการสมัครสมาชิกได้",
|
||||
"Failed to load subscription plans.": "ไม่สามารถโหลดแผนการสมัครสมาชิกได้",
|
||||
"year": "ปี",
|
||||
"Failed to create checkout session": "ไม่สามารถสร้างเซสชันการชำระเงินได้",
|
||||
"Storage": "พื้นที่เก็บข้อมูล",
|
||||
"Terms of Service": "ข้อกำหนดในการให้บริการ",
|
||||
"Privacy Policy": "นโยบายความเป็นส่วนตัว",
|
||||
"Disable Double Click": "ปิดการคลิกสองครั้ง",
|
||||
"TTS not supported for this document": "ไม่รองรับ TTS สำหรับเอกสารนี้",
|
||||
"Reset Password": "รีเซ็ตพาสเวิร์ด",
|
||||
"Show Reading Progress": "แสดงความคืบหน้าในการอ่าน",
|
||||
"Reading Progress Style": "สไตล์ความก้าวหน้าในการอ่าน",
|
||||
"Page Number": "หมายเลขหน้า",
|
||||
"Percentage": "เปอร์เซ็นต์",
|
||||
"Deleted local copy of the book: {{title}}": "ลบสำเนาในเครื่องแล้ว: {{title}}",
|
||||
"Failed to delete cloud backup of the book: {{title}}": "ไม่สามารถลบสำเนาสำรองในคลาวด์ของหนังสือ: {{title}}",
|
||||
"Failed to delete local copy of the book: {{title}}": "ไม่สามารถลบสำเนาในเครื่องของหนังสือ: {{title}}",
|
||||
"Are you sure to delete the local copy of the selected book?": "คุณแน่ใจหรือไม่ว่าต้องการลบสำเนาในเครื่องของหนังสือที่เลือก?",
|
||||
"Remove from Cloud & Device": "ลบจากคลาวด์และอุปกรณ์",
|
||||
"Remove from Cloud Only": "ลบจากคลาวด์เท่านั้น",
|
||||
"Remove from Device Only": "ลบจากอุปกรณ์เท่านั้น",
|
||||
"Disconnected": "ตัดการเชื่อมต่อ",
|
||||
"KOReader Sync Settings": "การตั้งค่าการซิงค์ KOReader",
|
||||
"Sync Strategy": "กลยุทธ์การซิงค์",
|
||||
"Ask on conflict": "ถามเมื่อเกิดความขัดแย้ง",
|
||||
"Always use latest": "ใช้เวอร์ชันล่าสุดเสมอ",
|
||||
"Send changes only": "ส่งการเปลี่ยนแปลงเท่านั้น",
|
||||
"Receive changes only": "รับการเปลี่ยนแปลงเท่านั้น",
|
||||
"Disabled": "ปิดใช้งาน",
|
||||
"Checksum Method": "วิธีการตรวจสอบความถูกต้อง",
|
||||
"File Content (recommended)": "เนื้อหาไฟล์ (แนะนำ)",
|
||||
"File Name": "ชื่อไฟล์",
|
||||
"Device Name": "ชื่ออุปกรณ์",
|
||||
"Sync Tolerance": "ความทนทานต่อการซิงค์",
|
||||
"Connect to your KOReader Sync server.": "เชื่อมต่อกับเซิร์ฟเวอร์ KOReader Sync ของคุณ",
|
||||
"Server URL": "URL ของเซิร์ฟเวอร์",
|
||||
"Username": "ชื่อผู้ใช้",
|
||||
"Your Username": "ชื่อผู้ใช้ของคุณ",
|
||||
"Password": "รหัสผ่าน",
|
||||
"Connect": "เชื่อมต่อ",
|
||||
"KOReader Sync": "KOReader Sync",
|
||||
"Sync Conflict": "ความขัดแย้งในการซิงค์",
|
||||
"Sync reading progress from \"{{deviceName}}\"?": "ซิงค์ความก้าวหน้าในการอ่านจาก \"{{deviceName}}\"?",
|
||||
"another device": "อุปกรณ์อื่น",
|
||||
"Local Progress": "ความก้าวหน้าในเครื่อง",
|
||||
"Remote Progress": "ความก้าวหน้าในคลาวด์",
|
||||
"Page {{page}} of {{total}} ({{percentage}}%)": "หน้า {{page}} จาก {{total}} ({{percentage}}%)",
|
||||
"Current position": "ตำแหน่งปัจจุบัน",
|
||||
"Approximately page {{page}} of {{total}} ({{percentage}}%)": "ประมาณหน้า {{page}} จาก {{total}} ({{percentage}}%)",
|
||||
"Approximately {{percentage}}%": "ประมาณ {{percentage}}%",
|
||||
"Failed to connect": "ไม่สามารถเชื่อมต่อได้",
|
||||
"Sync Server Connected": "เซิร์ฟเวอร์ซิงค์เชื่อมต่อแล้ว",
|
||||
"Precision: {{precision}} digits after the decimal": "ความแม่นยำ: {{precision}} หลักหลังจุดทศนิยม",
|
||||
"Sync as {{userDisplayName}}": "ซิงค์เป็น {{userDisplayName}}"
|
||||
}
|
||||
@@ -18,7 +18,6 @@
|
||||
"Confirm Deletion": "Silmeyi Onayla",
|
||||
"Copied to notebook": "Not defterine kopyalandı",
|
||||
"Copy": "Kopyala",
|
||||
"Custom CSS": "Özel CSS",
|
||||
"Dark Mode": "Karanlık Mod",
|
||||
"Default": "Varsayılan",
|
||||
"Default Font": "Varsayılan Yazı Tipi",
|
||||
@@ -28,7 +27,6 @@
|
||||
"Dictionary": "Sözlük",
|
||||
"Download Readest": "Readest'i İndir",
|
||||
"Edit": "Düzenle",
|
||||
"Enter your custom CSS here...": "Özel CSS'nizi buraya girin...",
|
||||
"Excerpts": "Alıntılar",
|
||||
"Failed to import book(s): {{filenames}}": "Kitap(lar) içe aktarılamadı: {{filenames}}",
|
||||
"Fast": "Hızlı",
|
||||
@@ -48,7 +46,6 @@
|
||||
"Horizontal Direction": "Yatay Yön",
|
||||
"Hyphenation": "Heceleme",
|
||||
"Import Books": "Kitapları İçe Aktar",
|
||||
"Language:": "Dil:",
|
||||
"Layout": "Düzen",
|
||||
"Light Mode": "Aydınlık Mod",
|
||||
"Loading...": "Yükleniyor...",
|
||||
@@ -70,8 +67,8 @@
|
||||
"Paging Animation": "Sayfa Çevirme Animasyonu",
|
||||
"Paragraph": "Paragraf",
|
||||
"Parallel Read": "Paralel Okuma",
|
||||
"Published:": "Yayınlanma:",
|
||||
"Publisher:": "Yayıncı:",
|
||||
"Published": "Yayınlanma",
|
||||
"Publisher": "Yayıncı",
|
||||
"Reading Progress Synced": "Okuma ilerlemesi senkronize edildi",
|
||||
"Reload Page": "Sayfayı Yenile",
|
||||
"Reveal in File Explorer": "Dosya Gezgininde Göster",
|
||||
@@ -96,16 +93,15 @@
|
||||
"Slow": "Yavaş",
|
||||
"Solarized": "Solarized",
|
||||
"Speak": "Konuş",
|
||||
"Subjects:": "Konular:",
|
||||
"Subjects": "Konular",
|
||||
"System Fonts": "Sistem Yazı Tipleri",
|
||||
"TTS not supported in this device": "Bu cihazda TTS desteklenmiyor",
|
||||
"Theme Color": "Tema Rengi",
|
||||
"Theme Mode": "Tema Modu",
|
||||
"Translate": "Çevir",
|
||||
"Translated Text": "Çevrilen Metin",
|
||||
"Unknown": "Bilinmiyor",
|
||||
"Untitled": "Başlıksız",
|
||||
"Updated:": "Güncellendi:",
|
||||
"Updated": "Güncellendi",
|
||||
"Version {{version}}": "Sürüm {{version}}",
|
||||
"Vertical Direction": "Dikey Yön",
|
||||
"Welcome to your library. You can import your books here and read them anytime.": "Kütüphanenize hoş geldiniz. Buradan kitaplarınızı içe aktarabilir ve istediğiniz zaman okuyabilirsiniz.",
|
||||
@@ -114,8 +110,6 @@
|
||||
"Your Library": "Kütüphaneniz",
|
||||
"TTS not supported for PDF": "PDF için TTS desteklenmiyor",
|
||||
"Override Book Font": "Kitap Yazı Tipini Geçersiz Kıl",
|
||||
"Vertical Margins (px)": "Dikey Kenar Boşlukları (px)",
|
||||
"Horizontal Margins (%)": "Yatay Kenar Boşlukları (%)",
|
||||
"Apply to All Books": "Tüm kitaplara uygula",
|
||||
"Apply to This Book": "Bu kitaba uygula",
|
||||
"Unable to fetch the translation. Try again later.": "Çeviri alınamıyor. Daha sonra tekrar deneyin.",
|
||||
@@ -130,8 +124,6 @@
|
||||
"Failed to download book: {{title}}": "Kitap indirilemedi: {{title}}",
|
||||
"Upload Book": "Kitap Yükle",
|
||||
"Auto Upload Books to Cloud": "Kitapları Otomatik Olarak Buluta Yükle",
|
||||
"{{percentage}}% of Cloud Storage Used.": "Bulut Depolamanın %{{percentage}}'si kullanıldı.",
|
||||
"Storage": "Depolama",
|
||||
"Book deleted: {{title}}": "Kitap silindi: {{title}}",
|
||||
"Failed to delete book: {{title}}": "Kitap silinemedi: {{title}}",
|
||||
"Check Updates on Start": "Başlangıçta Güncellemeleri Kontrol Et",
|
||||
@@ -163,7 +155,7 @@
|
||||
"Create a Password": "Şifre oluştur",
|
||||
"Sign up": "Kaydol",
|
||||
"Signing up...": "Kaydolunuyor...",
|
||||
"Don’t have an account? Sign up": "Hesabınız yok mu? Kaydolun",
|
||||
"Don't have an account? Sign up": "Hesabınız yok mu? Kaydolun",
|
||||
"Check your email for the confirmation link": "Onay bağlantısı için e-posta adresinizi kontrol edin",
|
||||
"Signing in ...": "Giriş yapılıyor...",
|
||||
"Send a magic link email": "Sihirli bağlantı e-postası gönder",
|
||||
@@ -187,35 +179,13 @@
|
||||
"Sign in with GitHub": "GitHub ile giriş yap",
|
||||
"Account": "Hesap",
|
||||
"Failed to delete user. Please try again later.": "Kullanıcı silinemedi. Lütfen daha sonra tekrar deneyin.",
|
||||
"Unlimited Offline/Online Reading": "Sınırsız Çevrimdışı/Çevrimiçi Okuma",
|
||||
"Unlimited Cloud Sync Devices": "Sınırsız Bulut Senkronizasyon Cihazları",
|
||||
"Essential Text-to-Speech Voices": "Temel Metinden Sese Dönüştürme Sesleri",
|
||||
"DeepL Free Access": "DeepL Ücretsiz Erişim",
|
||||
"Community Support": "Topluluk Desteği",
|
||||
"500 MB Cloud Sync Space": "500 MB Bulut Senkronizasyon Alanı",
|
||||
"Includes All Free Tier Benefits": "Tüm Ücretsiz Paket Avantajlarını İçerir",
|
||||
"AI Summaries": "Yapay Zeka Özetleri",
|
||||
"AI Translations": "Yapay Zeka Çevirileri",
|
||||
"Priority Support": "Öncelikli Destek",
|
||||
"DeepL Pro Access": "DeepL Pro Erişimi",
|
||||
"Expanded Text-to-Speech Voices": "Genişletilmiş Metinden Sese Dönüştürme Sesleri",
|
||||
"2000 MB Cloud Sync Space": "2000 MB Bulut Senkronizasyon Alanı",
|
||||
"Includes All Plus Tier Benefits": "Tüm Plus Paket Avantajlarını İçerir",
|
||||
"Unlimited AI Summaries": "Sınırsız Yapay Zeka Özetleri",
|
||||
"Unlimited AI Translations": "Sınırsız Yapay Zeka Çevirileri",
|
||||
"Unlimited DeepL Pro Access": "Sınırsız DeepL Pro Erişimi",
|
||||
"Advanced AI Tools": "Gelişmiş Yapay Zeka Araçları",
|
||||
"Early Feature Access": "Erken Özellik Erişimi",
|
||||
"10 GB Cloud Sync Space": "10 GB Bulut Senkronizasyon Alanı",
|
||||
"Loading profile...": "Profil yükleniyor...",
|
||||
"Features": "Özellikler",
|
||||
"Delete Account": "Hesabı Sil",
|
||||
"Delete Your Account?": "Hesabınızı Silmek İstiyor musunuz?",
|
||||
"This action cannot be undone. All your data in the cloud will be permanently deleted.": "Bu işlem geri alınamaz. Buluttaki tüm verileriniz kalıcı olarak silinecektir.",
|
||||
"Delete Permanently": "Kalıcı Olarak Sil",
|
||||
"Free Tier": "Ücretsiz Paket",
|
||||
"Plus Tier": "Plus Paket",
|
||||
"Pro Tier": "Pro Paket",
|
||||
"RTL Direction": "RTL Yönü",
|
||||
"Maximum Column Height": "Maksimum Sütun Yüksekliği",
|
||||
"Maximum Column Width": "Maksimum Sütun Genişliği",
|
||||
@@ -266,13 +236,13 @@
|
||||
"Clear Search": "Aramayı Temizle",
|
||||
"Header & Footer": "Başlık ve Altbilgi",
|
||||
"Apply also in Scrolled Mode": "Kaydırılmış Modda da Uygula",
|
||||
"A new version of Readest is Available!": "Yeni bir Readest sürümü mevcut!",
|
||||
"A new version of Readest is available!": "Yeni bir Readest sürümü mevcut!",
|
||||
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} mevcut (yüklü sürüm {{currentVersion}}).",
|
||||
"Download and install now?": "Şimdi indirip yükleyelim mi?",
|
||||
"Downloading {{downloaded}} of {{contentLength}}": "İndiriliyor {{downloaded}}/{{contentLength}}",
|
||||
"Download finished": "İndirme tamamlandı",
|
||||
"DOWNLOAD & INSTALL": "İNDİR & YÜKLE",
|
||||
"Changelog:": "Değişiklik Günlüğü:",
|
||||
"Changelog": "Değişiklik Günlüğü",
|
||||
"Software Update": "Yazılım Güncellemesi",
|
||||
"Title": "Başlık",
|
||||
"Date Read": "Okunma Tarihi",
|
||||
@@ -281,10 +251,9 @@
|
||||
"Ascending": "Artan",
|
||||
"Descending": "Azalan",
|
||||
"Sort by...": "Şuna göre sırala...",
|
||||
"Added:": "Eklenme:",
|
||||
"Format:": "Biçim:",
|
||||
"Added": "Eklenme",
|
||||
"GuanKiapTsingKhai-T": "GuanKiapTsingKhai-T",
|
||||
"Description:": "Tanım:",
|
||||
"Description": "Tanım",
|
||||
"No description available": "Tanım mevcut değil",
|
||||
"List": "Liste",
|
||||
"Grid": "Izgara",
|
||||
@@ -301,7 +270,7 @@
|
||||
"Checking for updates...": "Yenilikler kontrol ediliyor...",
|
||||
"Error checking for updates": "Güncellemeleri kontrol ederken hata oluştu",
|
||||
"Details": "Detaylar",
|
||||
"File Size:": "Dosya Boyutu:",
|
||||
"File Size": "Dosya Boyutu",
|
||||
"Auto Detect": "Algıla",
|
||||
"Next Section": "Sonraki Bölüm",
|
||||
"Previous Section": "Önceki Bölüm",
|
||||
@@ -342,14 +311,12 @@
|
||||
"Synced at {{time}}": "{{time}}'de eşzlendi",
|
||||
"Never synced": "Hiç eşzlenmedi",
|
||||
"Show Remaining Time": "Kalan Süreyi Göster",
|
||||
"Show Page Number": "Sayfa Numarasını Göster",
|
||||
"{{time}} min left in chapter": "{{time}} dakika bölümde kaldı",
|
||||
"Override Book Color": "Kitap Rengini Geçersiz Kıl",
|
||||
"Login Required": "Giriş gerekli",
|
||||
"Quota Exceeded": "Kota aşıldı",
|
||||
"{{percentage}}% of Daily Translation Characters Used.": "Çeviri için günlük karakterlerin %{{percentage}}'si kullanıldı.",
|
||||
"Translation Characters": "Çeviri Karakterleri",
|
||||
"Daily translation quota reached. Select another translate service to proceed.": "Günlük çeviri kotası aşıldı. Devam etmek için başka bir çeviri hizmeti seçin.",
|
||||
"{{engine}}: {{count}} voices_one": "{{engine}}: {{count}} ses",
|
||||
"{{engine}}: {{count}} voices_other": "{{engine}}: {{count}} sesler",
|
||||
"Something went wrong. Don't worry, our team has been notified and we're working on a fix.": "Bir şeyler yanlış gitti. Endişelenmeyin, ekibimiz bilgilendirildi ve bir çözüm üzerinde çalışıyoruz.",
|
||||
@@ -359,5 +326,183 @@
|
||||
"Contact Support": "Destekle İletişime Geç",
|
||||
"Code Highlighting": "Code Vurgulama",
|
||||
"Enable Highlighting": "Vurgulamayı Etkinleştir",
|
||||
"Code Language": "Code Dili"
|
||||
"Code Language": "Code Dili",
|
||||
"Top Margin (px)": "Üst Kenar Boşluğu (px)",
|
||||
"Bottom Margin (px)": "Alt Kenar Boşluğu (px)",
|
||||
"Right Margin (px)": "Sağ Kenar Boşluğu (px)",
|
||||
"Left Margin (px)": "Sol Kenar Boşluğu (px)",
|
||||
"Column Gap (%)": "Sütun Aralığı (%)",
|
||||
"Always Show Status Bar": "Durum Çubuğunu Her Zaman Göster",
|
||||
"Translation Not Available": "Çeviri Mevcut Değil",
|
||||
"Custom Content CSS": "Özel İçerik CSS'si",
|
||||
"Enter CSS for book content styling...": "Kitap içeriği stili için CSS girin...",
|
||||
"Custom Reader UI CSS": "Özel Okuyucu Arayüzü CSS'si",
|
||||
"Enter CSS for reader interface styling...": "Okuyucu arayüzü stili için CSS girin...",
|
||||
"Crop": "Kes",
|
||||
"Book Covers": "Kitap Kapağı",
|
||||
"Fit": "Uygun",
|
||||
"Reset {{settings}}": "{{settings}}'i Sıfırla",
|
||||
"Reset Settings": "Ayarları Sıfırla",
|
||||
"{{count}} pages left in chapter_one": "Bu bölümde {{count}} sayfa kaldı",
|
||||
"{{count}} pages left in chapter_other": "Bu bölümde {{count}} sayfa kaldı",
|
||||
"Show Remaining Pages": "Kalan sayfaları göster",
|
||||
"Source Han Serif CN VF": "Source Han Serif",
|
||||
"Huiwen-mincho": "Huiwen Mincho",
|
||||
"KingHwa_OldSong": "KingHwa Song",
|
||||
"Manage Subscription": "Aboneliği Yönet",
|
||||
"Coming Soon": "Yakında",
|
||||
"Upgrade to {{plan}}": "{{plan}} planına yükselt",
|
||||
"Upgrade to Plus or Pro": "Plus veya Pro'ya yükselt",
|
||||
"Current Plan": "Mevcut Plan",
|
||||
"Plan Limits": "Plan Limitleri",
|
||||
"Processing your payment...": "Ödemeniz işleniyor...",
|
||||
"Please wait while we confirm your subscription.": "Aboneliğinizi onaylıyoruz, lütfen bekleyin.",
|
||||
"Payment Processing": "Ödeme İşleniyor",
|
||||
"Your payment is being processed. This usually takes a few moments.": "Ödemeniz işleniyor. Bu genelde birkaç saniye sürer.",
|
||||
"Payment Failed": "Ödeme Başarısız",
|
||||
"We couldn't process your subscription. Please try again or contact support if the issue persists.": "Aboneliğinizi işleyemedik. Lütfen tekrar deneyin veya sorun devam ederse destek ile iletişime geçin.",
|
||||
"Back to Profile": "Profille Dön",
|
||||
"Subscription Successful!": "Abonelik Başarılı!",
|
||||
"Thank you for your subscription! Your payment has been processed successfully.": "Aboneliğiniz için teşekkürler! Ödemeniz başarıyla işlendi.",
|
||||
"Email:": "E-posta:",
|
||||
"Plan:": "Plan:",
|
||||
"Amount:": "Tutar:",
|
||||
"Subscription ID:": "Abonelik ID'si:",
|
||||
"Go to Library": "Kütüphaneye Git",
|
||||
"Need help? Contact our support team at support@readest.com": "Yardım mı gerekiyor? Destek ekibimizle iletişime geçin: support@readest.com",
|
||||
"Free Plan": "Ücretsiz Plan",
|
||||
"month": "ay",
|
||||
"AI Translations (per day)": "AI Çevirileri (günlük)",
|
||||
"Plus Plan": "Plus Plan",
|
||||
"Includes All Free Plan Benefits": "Tüm Ücretsiz Plan Özelliklerini İçerir",
|
||||
"Pro Plan": "Pro Plan",
|
||||
"More AI Translations": "Daha Fazla AI Çevirisi",
|
||||
"Complete Your Subscription": "Aboneliğinizi Tamamlayın",
|
||||
"{{percentage}}% of Cloud Sync Space Used.": "Bulut Senkronizasyon Alanının %{{percentage}}'si Kullanıldı.",
|
||||
"Cloud Sync Storage": "Bulut Senkronizasyon Depolama",
|
||||
"Disable": "Devre Dışı Bırak",
|
||||
"Enable": "Etkinleştir",
|
||||
"Upgrade to Readest Premium": "Readest Premium’a Yükselt",
|
||||
"Show Source Text": "Kaynak Metni Göster",
|
||||
"Cross-Platform Sync": "Platformlar Arası Senkronizasyon",
|
||||
"Seamlessly sync your library, progress, highlights, and notes across all your devices—never lose your place again.": "Kütüphanenizi, ilerlemenizi, vurgularınızı ve notlarınızı tüm cihazlarınızda sorunsuzca senkronize edin—bir daha asla yerinizi kaybetmeyin.",
|
||||
"Customizable Reading": "Özelleştirilebilir Okuma",
|
||||
"Personalize every detail with adjustable fonts, layouts, themes, and advanced display settings for the perfect reading experience.": "Mükemmel okuma deneyimi için ayarlanabilir yazı tipleri, düzenler, temalar ve gelişmiş görüntü ayarlarıyla her detayı kişiselleştirin.",
|
||||
"AI Read Aloud": "AI Sesli Okuma",
|
||||
"Enjoy hands-free reading with natural-sounding AI voices that bring your books to life.": "Kitaplarınızı hayata geçiren doğal sesli AI sesleriyle eller serbest okuma keyfini çıkarın.",
|
||||
"AI Translations": "AI Çeviriler",
|
||||
"Translate any text instantly with the power of Google, Azure, or DeepL—understand content in any language.": "Google, Azure veya DeepL'in gücüyle herhangi bir metni anında çevirin—her dildeki içeriği anlayın.",
|
||||
"Connect with fellow readers and get help fast in our friendly community channels.": "Diğer okuyucularla bağlantı kurun ve dostane topluluk kanallarımızda hızla yardım alın.",
|
||||
"Unlimited AI Read Aloud Hours": "Sınırsız AI Sesli Okuma Saatleri",
|
||||
"Listen without limits—convert as much text as you like into immersive audio.": "Sınırsız dinleyin—istediğiniz kadar metni sürükleyici sese dönüştürün.",
|
||||
"Unlock enhanced translation capabilities with more daily usage and advanced options.": "Daha fazla günlük kullanım ve gelişmiş seçeneklerle gelişmiş çeviri yeteneklerinin kilidini açın.",
|
||||
"DeepL Pro Access": "DeepL Pro Erişimi",
|
||||
"Enjoy faster responses and dedicated assistance whenever you need help.": "Yardıma ihtiyacınız olduğunda daha hızlı yanıtlar ve özel destek keyfini çıkarın.",
|
||||
"Daily translation quota reached. Upgrade your plan to continue using AI translations.": "Günlük çeviri kotasına ulaşıldı. AI çevirileri kullanmaya devam etmek için planınızı yükseltin.",
|
||||
"Includes All Plus Plan Benefits": "Tüm Plus Plan Avantajları Dahil",
|
||||
"Early Feature Access": "Erken Özellik Erişimi",
|
||||
"Be the first to explore new features, updates, and innovations before anyone else.": "Yeni özellikleri, güncellemeleri ve yenilikleri herkesten önce keşfedin.",
|
||||
"Advanced AI Tools": "Gelişmiş AI Araçları",
|
||||
"Harness powerful AI tools for smarter reading, translation, and content discovery.": "Akıllı okuma, çeviri ve içerik keşfi için güçlü AI araçlarını kullanın.",
|
||||
"Translate up to 100,000 characters daily with the most accurate translation engine available.": "Mevcut en doğru çeviri motoruyla günlük 100.000 karaktere kadar çeviri yapın.",
|
||||
"Translate up to 500,000 characters daily with the most accurate translation engine available.": "Mevcut en doğru çeviri motoruyla günlük 500.000 karaktere kadar çeviri yapın.",
|
||||
"Securely store and access your entire reading collection with up to 5 GB of cloud storage.": "Tüm okuma koleksiyonunuzu 5 GB'a kadar güvenli bulut depolamayla saklayın ve erişin.",
|
||||
"Securely store and access your entire reading collection with up to 20 GB of cloud storage.": "Tüm okuma koleksiyonunuzu 20 GB'a kadar güvenli bulut depolamayla saklayın ve erişin.",
|
||||
"Deleted cloud backup of the book: {{title}}": "Kitabın bulut yedeği silindi: {{title}}",
|
||||
"Are you sure to delete the cloud backup of the selected book?": "Seçilen kitabın bulut yedeğini silmek istediğinize emin misiniz?",
|
||||
"What's New in Readest": "Readest'te Neler Yeni",
|
||||
"Enter book title": "Kitap başlığını girin",
|
||||
"Subtitle": "Alt başlık",
|
||||
"Enter book subtitle": "Kitap alt başlığını girin",
|
||||
"Enter author name": "Yazar adını girin",
|
||||
"Series": "Seri",
|
||||
"Enter series name": "Seri adını girin",
|
||||
"Series Index": "Seri numarası",
|
||||
"Enter series index": "Seri numarasını girin",
|
||||
"Total in Series": "Serideki toplam",
|
||||
"Enter total books in series": "Serideki toplam kitap sayısını girin",
|
||||
"Enter publisher": "Yayınevini girin",
|
||||
"Publication Date": "Yayın tarihi",
|
||||
"Identifier": "Tanımlayıcı",
|
||||
"Enter book description": "Kitap açıklamasını girin",
|
||||
"Change cover image": "Kapak resmini değiştir",
|
||||
"Replace": "Değiştir",
|
||||
"Unlock cover": "Kapağın kilidini aç",
|
||||
"Lock cover": "Kapağı kilitle",
|
||||
"Auto-Retrieve Metadata": "Meta verileri otomatik al",
|
||||
"Auto-Retrieve": "Otomatik alma",
|
||||
"Unlock all fields": "Tüm alanların kilidini aç",
|
||||
"Unlock All": "Tümünün kilidini aç",
|
||||
"Lock all fields": "Tüm alanları kilitle",
|
||||
"Lock All": "Tümünü kilitle",
|
||||
"Reset": "Sıfırla",
|
||||
"Edit Metadata": "Meta verileri düzenle",
|
||||
"Locked": "Kilitli",
|
||||
"Select Metadata Source": "Meta veri kaynağını seç",
|
||||
"Keep manual input": "Manuel girişi koru",
|
||||
"Google Books": "Google Books",
|
||||
"Open Library": "Open Library",
|
||||
"Fiction, Science, History": "Kurgu, Bilim, Tarih",
|
||||
"Select Cover Image": "Kapak Resmi Seç",
|
||||
"Open Book in New Window": "Kitabı Yeni Pencerede Aç",
|
||||
"Voices for {{lang}}": "{{lang}} için Sesler",
|
||||
"Yandex Translate": "Yandex Çeviri",
|
||||
"YYYY or YYYY-MM-DD": "YYYY veya YYYY-MM-DD",
|
||||
"Restore Purchase": "Satın Almayı Geri Yükle",
|
||||
"No purchases found to restore.": "Geri yüklemek için satın alma bulunamadı.",
|
||||
"Failed to restore purchases.": "Satın alımları geri yükleme başarısız oldu.",
|
||||
"Failed to manage subscription.": "Aboneliği yönetme başarısız oldu.",
|
||||
"Failed to load subscription plans.": "Abonelik planlarını yükleme başarısız oldu.",
|
||||
"year": "yıl",
|
||||
"Failed to create checkout session": "Ödeme oturumu oluşturma başarısız oldu",
|
||||
"Storage": "Depolama",
|
||||
"Terms of Service": "Hizmet Şartları",
|
||||
"Privacy Policy": "Gizlilik Politikası",
|
||||
"Disable Double Click": "Çift Tıklamayı Devre Dışı Bırak",
|
||||
"TTS not supported for this document": "TTS bu belge için desteklenmiyor",
|
||||
"Reset Password": "Şifreyi Sıfırla",
|
||||
"Show Reading Progress": "Okuma İlerlemesini Göster",
|
||||
"Reading Progress Style": "Okuma İlerlemesi Stili",
|
||||
"Page Number": "Sayfa Numarası",
|
||||
"Percentage": "Yüzde",
|
||||
"Deleted local copy of the book: {{title}}": "Kitabın yerel kopyası silindi: {{title}}",
|
||||
"Failed to delete cloud backup of the book: {{title}}": "Kitabın bulut yedeğini silme başarısız oldu: {{title}}",
|
||||
"Failed to delete local copy of the book: {{title}}": "Kitabın yerel kopyasını silme başarısız oldu: {{title}}",
|
||||
"Are you sure to delete the local copy of the selected book?": "Seçilen kitabın yerel kopyasını silmek istediğinize emin misiniz?",
|
||||
"Remove from Cloud & Device": "Buluttan ve Cihazdan Kaldır",
|
||||
"Remove from Cloud Only": "Sadece Buluttan Kaldır",
|
||||
"Remove from Device Only": "Sadece Cihazdan Kaldır",
|
||||
"Disconnected": "Bağlantı Kesildi",
|
||||
"KOReader Sync Settings": "KOReader Senkronizasyon Ayarları",
|
||||
"Sync Strategy": "Senkronizasyon Stratejisi",
|
||||
"Ask on conflict": "Çatışmada Sor",
|
||||
"Always use latest": "Her Zaman En Sonunu Kullan",
|
||||
"Send changes only": "Sadece Değişiklikleri Gönder",
|
||||
"Receive changes only": "Sadece Değişiklikleri Al",
|
||||
"Disabled": "Devre Dışı",
|
||||
"Checksum Method": "Kontrol Toplamı Yöntemi",
|
||||
"File Content (recommended)": "Dosya İçeriği (önerilen)",
|
||||
"File Name": "Dosya Adı",
|
||||
"Device Name": "Cihaz Adı",
|
||||
"Sync Tolerance": "Senkronizasyon Toleransı",
|
||||
"Connect to your KOReader Sync server.": "KOReader Senkronizasyon sunucunuza bağlanın.",
|
||||
"Server URL": "Sunucu URL'si",
|
||||
"Username": "Kullanıcı Adı",
|
||||
"Your Username": "Kullanıcı Adınız",
|
||||
"Password": "Şifre",
|
||||
"Connect": "Bağlan",
|
||||
"KOReader Sync": "KOReader Senkronizasyon",
|
||||
"Sync Conflict": "Senkronizasyon Çatışması",
|
||||
"Sync reading progress from \"{{deviceName}}\"?": "\"{{deviceName}}\" cihazından okuma ilerlemesini senkronize etmek istiyor musunuz?",
|
||||
"another device": "başka bir cihaz",
|
||||
"Local Progress": "Yerel İlerleme",
|
||||
"Remote Progress": "Uzak İlerleme",
|
||||
"Page {{page}} of {{total}} ({{percentage}}%)": "Sayfa {{page}} / {{total}} ({{percentage}}%)",
|
||||
"Current position": "Mevcut konum",
|
||||
"Approximately page {{page}} of {{total}} ({{percentage}}%)": "Yaklaşık sayfa {{page}} / {{total}} ({{percentage}}%)",
|
||||
"Approximately {{percentage}}%": "Yaklaşık {{percentage}}%",
|
||||
"Failed to connect": "Bağlanma başarısız oldu",
|
||||
"Sync Server Connected": "Senkronizasyon Sunucusu Bağlandı",
|
||||
"Precision: {{precision}} digits after the decimal": "Hassasiyet: {{precision}} ondalık basamaktan sonra",
|
||||
"Sync as {{userDisplayName}}": "Senkronize et {{userDisplayName}} olarak"
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"(detected)": "(виявлено)",
|
||||
"About Readest": "Про Readest",
|
||||
"Add your notes here...": "Додайте свої нотатки тут...",
|
||||
"Add your notes here...": "Додайте свої нотатки сюди...",
|
||||
"Animation": "Анімація",
|
||||
"Annotate": "Анотація",
|
||||
"Apply": "Застосувати",
|
||||
@@ -18,7 +18,6 @@
|
||||
"Confirm Deletion": "Підтвердити видалення",
|
||||
"Copied to notebook": "Скопійовано до нотатника",
|
||||
"Copy": "Копіювати",
|
||||
"Custom CSS": "Користувацький CSS",
|
||||
"Dark Mode": "Темний режим",
|
||||
"Default": "За замовчуванням",
|
||||
"Default Font": "Шрифт за замовчуванням",
|
||||
@@ -28,7 +27,6 @@
|
||||
"Dictionary": "Словник",
|
||||
"Download Readest": "Завантажити Readest",
|
||||
"Edit": "Редагувати",
|
||||
"Enter your custom CSS here...": "Введіть ваш користувацький CSS тут...",
|
||||
"Excerpts": "Уривки",
|
||||
"Failed to import book(s): {{filenames}}": "Не вдалося імпортувати книгу(и): {{filenames}}",
|
||||
"Fast": "Швидко",
|
||||
@@ -48,33 +46,32 @@
|
||||
"Horizontal Direction": "Горизонтальний напрямок",
|
||||
"Hyphenation": "Перенесення слів",
|
||||
"Import Books": "Імпортувати книги",
|
||||
"Language:": "Мова:",
|
||||
"Layout": "Макет",
|
||||
"Light Mode": "Світлий режим",
|
||||
"Loading...": "Завантаження...",
|
||||
"Logged in": "Увійшли",
|
||||
"Logged in as {{userDisplayName}}": "Увійшли як {{userDisplayName}}",
|
||||
"Match Case": "Враховувати регістр",
|
||||
"Logged in": "Увійдено",
|
||||
"Logged in as {{userDisplayName}}": "Увійдено як {{userDisplayName}}",
|
||||
"Match Case": "Враховувати реґістр",
|
||||
"Match Diacritics": "Враховувати діакритичні знаки",
|
||||
"Match Whole Words": "Шукати цілі слова",
|
||||
"Maximum Number of Columns": "Максимальна кількість колонок",
|
||||
"Minimum Font Size": "Мінімальний розмір шрифту",
|
||||
"Monospace Font": "Моноширинний шрифт",
|
||||
"More Info": "Більше інформації",
|
||||
"Nord": "Північний",
|
||||
"Nord": "Норд",
|
||||
"Notebook": "Нотатник",
|
||||
"Notes": "Нотатки",
|
||||
"Open": "Відкрити",
|
||||
"Original Text": "Оригінальний текст",
|
||||
"Original Text": "Ориґінальний текст",
|
||||
"Page": "Сторінка",
|
||||
"Paging Animation": "Анімація гортання",
|
||||
"Paragraph": "Абзац",
|
||||
"Parallel Read": "Паралельне читання",
|
||||
"Published:": "Опубліковано:",
|
||||
"Publisher:": "Видавець:",
|
||||
"Published": "Опубліковано",
|
||||
"Publisher": "Видавець",
|
||||
"Reading Progress Synced": "Прогрес читання синхронізовано",
|
||||
"Reload Page": "Перезавантажити сторінку",
|
||||
"Reveal in File Explorer": "Показати у провіднику",
|
||||
"Reveal in File Explorer": "Показати у Файловому менеджері",
|
||||
"Reveal in Finder": "Показати у Finder",
|
||||
"Reveal in Folder": "Показати в папці",
|
||||
"Sans-Serif Font": "Шрифт без засічок",
|
||||
@@ -96,16 +93,15 @@
|
||||
"Slow": "Повільно",
|
||||
"Solarized": "Сонячний",
|
||||
"Speak": "Озвучити",
|
||||
"Subjects:": "Теми:",
|
||||
"Subjects": "Жанри",
|
||||
"System Fonts": "Системні шрифти",
|
||||
"TTS not supported in this device": "TTS не підтримується на цьому пристрої",
|
||||
"Theme Color": "Колір теми",
|
||||
"Theme Mode": "Режим теми",
|
||||
"Translate": "Перекласти",
|
||||
"Translated Text": "Перекладений текст",
|
||||
"Unknown": "Невідомо",
|
||||
"Untitled": "Без назви",
|
||||
"Updated:": "Оновлено:",
|
||||
"Updated": "Оновлено",
|
||||
"Version {{version}}": "Версія {{version}}",
|
||||
"Vertical Direction": "Вертикальний напрямок",
|
||||
"Welcome to your library. You can import your books here and read them anytime.": "Ласкаво просимо до вашої бібліотеки. Ви можете імпортувати сюди свої книги та читати їх будь-коли.",
|
||||
@@ -114,13 +110,11 @@
|
||||
"Your Library": "Ваша бібліотека",
|
||||
"TTS not supported for PDF": "TTS не підтримується для PDF",
|
||||
"Override Book Font": "Перевизначити шрифт книги",
|
||||
"Vertical Margins (px)": "Вертикальні відступи (px)",
|
||||
"Horizontal Margins (%)": "Горизонтальні відступи (%)",
|
||||
"Apply to All Books": "Застосувати до всіх книг",
|
||||
"Apply to This Book": "Застосувати до цієї книги",
|
||||
"Apply to This Book": "Застосувати лише до цієї книги",
|
||||
"Unable to fetch the translation. Try again later.": "Не вдалося отримати переклад. Спробуйте пізніше.",
|
||||
"Check Update": "Перевірити оновлення",
|
||||
"Already the latest version": "Вже остання версія",
|
||||
"Already the latest version": "У вас вже встановлена остання версія",
|
||||
"Book Details": "Деталі книги",
|
||||
"From Local File": "З локального файлу",
|
||||
"TOC": "Зміст",
|
||||
@@ -130,12 +124,10 @@
|
||||
"Failed to download book: {{title}}": "Не вдалося завантажити книгу: {{title}}",
|
||||
"Upload Book": "Завантажити книгу",
|
||||
"Auto Upload Books to Cloud": "Автоматично завантажувати книги в хмару",
|
||||
"{{percentage}}% of Cloud Storage Used.": "{{percentage}}% використано хмарного сховища.",
|
||||
"Storage": "Сховище",
|
||||
"Book deleted: {{title}}": "Книгу видалено: {{title}}",
|
||||
"Failed to delete book: {{title}}": "Не вдалося видалити книгу: {{title}}",
|
||||
"Check Updates on Start": "Перевіряти оновлення при запуску",
|
||||
"Insufficient storage quota": "Недостатньо квоти сховища",
|
||||
"Insufficient storage quota": "Недостатньо місця у сховищі",
|
||||
"Font Weight": "Насиченість шрифту",
|
||||
"Line Spacing": "Міжрядковий інтервал",
|
||||
"Word Spacing": "Міжслівний інтервал",
|
||||
@@ -163,11 +155,11 @@
|
||||
"Create a Password": "Створити пароль",
|
||||
"Sign up": "Зареєструватися",
|
||||
"Signing up...": "Реєстрація...",
|
||||
"Don’t have an account? Sign up": "Немає акаунту? Зареєструйтеся",
|
||||
"Check your email for the confirmation link": "Перевірте свою електронну пошту для підтвердження посилання",
|
||||
"Don't have an account? Sign up": "Немає акаунту? Зареєструйтеся",
|
||||
"Check your email for the confirmation link": "Перевірте свою електронну пошту на посилання із підтвердженням",
|
||||
"Signing in ...": "Увійти...",
|
||||
"Send a magic link email": "Відправити магічне посилання на електронну пошту",
|
||||
"Check your email for the magic link": "Перевірте свою електронну пошту для магічного посилання",
|
||||
"Check your email for the magic link": "Ми надіслали вам магічне посилання. Перевірте свою електронну пошту",
|
||||
"Send reset password instructions": "Надіслати інструкції для скидання пароля",
|
||||
"Sending reset instructions ...": "Надсилаються інструкції для скидання пароля...",
|
||||
"Forgot your password?": "Забули пароль?",
|
||||
@@ -187,35 +179,13 @@
|
||||
"Sign in with GitHub": "Увійти через GitHub",
|
||||
"Account": "Обліковий запис",
|
||||
"Failed to delete user. Please try again later.": "Не вдалося видалити користувача. Будь ласка, спробуйте пізніше.",
|
||||
"Unlimited Offline/Online Reading": "Необмежене читання офлайн/онлайн",
|
||||
"Unlimited Cloud Sync Devices": "Необмежена кількість пристроїв для хмарної синхронізації",
|
||||
"Essential Text-to-Speech Voices": "Основні голоси для перетворення тексту в мовлення",
|
||||
"DeepL Free Access": "Безкоштовний доступ до DeepL",
|
||||
"Community Support": "Підтримка спільноти",
|
||||
"500 MB Cloud Sync Space": "500 МБ простору для хмарної синхронізації",
|
||||
"Includes All Free Tier Benefits": "Включає всі переваги безкоштовного тарифу",
|
||||
"AI Summaries": "AI-резюме",
|
||||
"AI Translations": "AI-переклади",
|
||||
"Priority Support": "Пріоритетна підтримка",
|
||||
"DeepL Pro Access": "Доступ до DeepL Pro",
|
||||
"Expanded Text-to-Speech Voices": "Розширений набір голосів для перетворення тексту в мовлення",
|
||||
"2000 MB Cloud Sync Space": "2000 МБ простору для хмарної синхронізації",
|
||||
"Includes All Plus Tier Benefits": "Включає всі переваги тарифу Plus",
|
||||
"Unlimited AI Summaries": "Необмежені AI-резюме",
|
||||
"Unlimited AI Translations": "Необмежені AI-переклади",
|
||||
"Unlimited DeepL Pro Access": "Необмежений доступ до DeepL Pro",
|
||||
"Advanced AI Tools": "Розширені AI-інструменти",
|
||||
"Early Feature Access": "Ранній доступ до нових функцій",
|
||||
"10 GB Cloud Sync Space": "10 ГБ простору для хмарної синхронізації",
|
||||
"Loading profile...": "Завантаження профілю...",
|
||||
"Features": "Функції",
|
||||
"Delete Account": "Видалити обліковий запис",
|
||||
"Delete Your Account?": "Видалити ваш обліковий запис?",
|
||||
"This action cannot be undone. All your data in the cloud will be permanently deleted.": "Цю дію неможливо скасувати. Всі ваші дані в хмарі будуть повністю видалені.",
|
||||
"Delete Permanently": "Видалити назавжди",
|
||||
"Free Tier": "Безкоштовний тариф",
|
||||
"Plus Tier": "Тариф Plus",
|
||||
"Pro Tier": "Тариф Pro",
|
||||
"RTL Direction": "Напрямок RTL",
|
||||
"Maximum Column Height": "Максимальна висота колонки",
|
||||
"Maximum Column Width": "Максимальна ширина колонки",
|
||||
@@ -224,7 +194,7 @@
|
||||
"No supported files found. Supported formats: {{formats}}": "Не знайдено підтримуваних файлів. Підтримувані формати: {{formats}}",
|
||||
"Drop to Import Books": "Перетягніть для імпорту книг",
|
||||
"Custom": "Користувацький",
|
||||
"All the world's a stage,\nAnd all the men and women merely players;\nThey have their exits and their entrances,\nAnd one man in his time plays many parts,\nHis acts being seven ages.\n\n— William Shakespeare": "Весь світ — театр,\nІ всі чоловіки та жінки — просто актори;\nУ них є свої виходи та входи,\nІ один чоловік у свій час грає багато ролей,\nЙого вчинки — це сім віков.\n\n— Вільям Шекспір",
|
||||
"All the world's a stage,\nAnd all the men and women merely players;\nThey have their exits and their entrances,\nAnd one man in his time plays many parts,\nHis acts being seven ages.\n\n— William Shakespeare": "Так, світ — театр,\nДе всі чоловіки й жінки — актори.\nТут кожному приписаний свій вихід,\nне одну з них кожне грає роль.\nСім дій в тій п’єсі...\nВільям Шекспір\n(переклад Олександра Мокровольського)",
|
||||
"Custom Theme": "Користувацька тема",
|
||||
"Theme Name": "Назва теми",
|
||||
"Text Color": "Колір тексту",
|
||||
@@ -233,8 +203,8 @@
|
||||
"Contrast": "Контраст",
|
||||
"Sunset": "Захід сонця",
|
||||
"Double Border": "Подвійна рамка",
|
||||
"Border Color": "Колір рамки",
|
||||
"Border Frame": "Рамка рамки",
|
||||
"Border Color": "Колір країв",
|
||||
"Border Frame": "Рамка по краям",
|
||||
"Show Header": "Показати заголовок",
|
||||
"Show Footer": "Показати нижню частину",
|
||||
"Small": "Малий",
|
||||
@@ -246,7 +216,7 @@
|
||||
"Exported from Readest": "Експортовано з Readest",
|
||||
"Highlights & Annotations": "Виділення та анотації",
|
||||
"Note": "Примітка",
|
||||
"Copied to clipboard": "Скопійовано в буфер обміну",
|
||||
"Copied to clipboard": "Скопійовано до буфера обміну",
|
||||
"Export Annotations": "Експортувати анотації",
|
||||
"Auto Import on File Open": "Автоматичний імпорт при відкритті файлу",
|
||||
"LXGW WenKai GB Screen": "LXGW WenKai SC",
|
||||
@@ -266,14 +236,14 @@
|
||||
"Clear Search": "Очистити пошук",
|
||||
"Header & Footer": "Заголовок і нижній колонтитул",
|
||||
"Apply also in Scrolled Mode": "Застосувати також у прокрученому режимі",
|
||||
"A new version of Readest is Available!": "Доступна нова версія Readest!",
|
||||
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} доступна (встановлена версія {{currentVersion}}).",
|
||||
"A new version of Readest is available!": "Доступна нова версія Readest!",
|
||||
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} доступна (встановлено {{currentVersion}}).",
|
||||
"Download and install now?": "Завантажити та встановити зараз?",
|
||||
"Downloading {{downloaded}} of {{contentLength}}": "Завантаження {{downloaded}} з {{contentLength}}",
|
||||
"Downloading {{downloaded}} of {{contentLength}}": "Завантаженно {{downloaded}} з {{contentLength}}",
|
||||
"Download finished": "Завантаження завершено",
|
||||
"DOWNLOAD & INSTALL": "ЗАВАНТАЖИТИ І ВСТАНОВИТИ",
|
||||
"Changelog:": "Журнал змін:",
|
||||
"Software Update": "Оновлення програмного забезпечення",
|
||||
"DOWNLOAD & INSTALL": "ЗАВАНТАЖИТИ ТА ВСТАНОВИТИ",
|
||||
"Changelog": "Список змін",
|
||||
"Software Update": "Оновлення ПЗ",
|
||||
"Title": "Назва",
|
||||
"Date Read": "Дата прочитання",
|
||||
"Date Added": "Дата додавання",
|
||||
@@ -281,27 +251,26 @@
|
||||
"Ascending": "За зростанням",
|
||||
"Descending": "За спаданням",
|
||||
"Sort by...": "Сортувати за...",
|
||||
"Added:": "Додано:",
|
||||
"Format:": "Формат:",
|
||||
"Added": "Додано",
|
||||
"GuanKiapTsingKhai-T": "GuanKiapTsingKhai-T",
|
||||
"Description:": "Опис:",
|
||||
"Description": "Опис",
|
||||
"No description available": "Опис недоступний",
|
||||
"List": "Список",
|
||||
"Grid": "Сітка",
|
||||
"(from 'As You Like It', Act II)": "(з 'Як вам це подобається', Акт II)",
|
||||
"(from 'As You Like It', Act II)": "(із 'Як вам це подобається', Дія II)",
|
||||
"Link Color": "Колір посилання",
|
||||
"Volume Keys for Page Flip": "Клавіші гучності для перевороту сторінки",
|
||||
"Clicks for Page Flip": "Клацання для перевороту сторінки",
|
||||
"Swap Clicks Area": "Змінити область клацання",
|
||||
"Volume Keys for Page Flip": "Перегортати сторінки клавішами гучності",
|
||||
"Clicks for Page Flip": "Перегортати сторінки натисканням",
|
||||
"Swap Clicks Area": "Поміняти області клацання місцями",
|
||||
"Screen": "Екран",
|
||||
"Orientation": "Орієнтація",
|
||||
"Portrait": "Портрет",
|
||||
"Landscape": "Ландшафт",
|
||||
"Open Last Book on Start": "Відкрити останню книгу при запуску",
|
||||
"Open Last Book on Start": "Відкривати останню книгу при запуску",
|
||||
"Checking for updates...": "Перевірка наявності оновлень...",
|
||||
"Error checking for updates": "Помилка перевірки наявності оновлень",
|
||||
"Details": "Деталі",
|
||||
"File Size:": "Розмір файлу:",
|
||||
"File Size": "Розмір файлу",
|
||||
"Auto Detect": "Авто виявлення",
|
||||
"Next Section": "Наступний розділ",
|
||||
"Previous Section": "Попередній розділ",
|
||||
@@ -315,22 +284,22 @@
|
||||
"Deselect": "Зняти",
|
||||
"Select All": "Усі",
|
||||
"No translation available.": "Переклад недоступний.",
|
||||
"Translated by {{provider}}.": "Перекладено {{provider}}.",
|
||||
"Translated by {{provider}}.": "Перекладено за допомогою {{provider}}.",
|
||||
"DeepL": "DeepL",
|
||||
"Google Translate": "Google Translate",
|
||||
"Azure Translator": "Azure Translator",
|
||||
"Invert Image In Dark Mode": "Інвертувати зображення в темряві",
|
||||
"Google Translate": "Google Перекладач",
|
||||
"Azure Translator": "Azure Перекладач",
|
||||
"Invert Image In Dark Mode": "Інвертувати зображення в темному режимі",
|
||||
"Help improve Readest": "Допоможіть покращити Readest",
|
||||
"Sharing anonymized statistics": "Поділ анонімною статистикою",
|
||||
"Sharing anonymized statistics": "Ділитись анонімною статистикою",
|
||||
"Interface Language": "Мова інтерфейсу",
|
||||
"Translation": "Переклад",
|
||||
"Enable Translation": "Увімкнути переклад",
|
||||
"Translation Service": "Служба перекладу",
|
||||
"Translate To": "Перекласти на",
|
||||
"Translate To": "Мова перекладу",
|
||||
"Disable Translation": "Вимкнути переклад",
|
||||
"Scroll": "Прокрутка",
|
||||
"Overlap Pixels": "Пікселі перекриття",
|
||||
"Click": "Клацнути",
|
||||
"Overlap Pixels": "Перекривати пікселі",
|
||||
"Click": "Натиснути",
|
||||
"System Language": "Системна мова",
|
||||
"Security": "Безпека",
|
||||
"Allow JavaScript": "Дозволити JavaScript",
|
||||
@@ -346,24 +315,202 @@
|
||||
"Synced at {{time}}": "Синхр. о {{time}}",
|
||||
"Never synced": "Ніколи не синхр.",
|
||||
"Show Remaining Time": "Показати залишок часу",
|
||||
"Show Page Number": "Показати номер сторінки",
|
||||
"{{time}} min left in chapter": "{{time}} хв до кінця розділу",
|
||||
"Override Book Color": "Перевизначити колір книги",
|
||||
"Login Required": "Потрібен вхід",
|
||||
"Quota Exceeded": "Перевищено ліміт",
|
||||
"{{percentage}}% of Daily Translation Characters Used.": "Використано {{percentage}}% символів перекладу за день.",
|
||||
"Translation Characters": "Символи перекладу",
|
||||
"Daily translation quota reached. Select another translate service to proceed.": "Досягнуто добового ліміту перекладу. Виберіть іншу службу перекладу для продовження.",
|
||||
"{{engine}}: {{count}} voices_one": "{{engine}}: {{count}} голос",
|
||||
"{{engine}}: {{count}} voices_few": "{{engine}}: {{count}} голоси",
|
||||
"{{engine}}: {{count}} voices_many": "{{engine}}: {{count}} голосів",
|
||||
"{{engine}}: {{count}} voices_other": "{{engine}}: {{count}} голоси",
|
||||
"Something went wrong. Don't worry, our team has been notified and we're working on a fix.": "Щось пішло не так. Не хвилюйтеся, наша команда була сповіщена, і ми працюємо над виправленням.",
|
||||
"Something went wrong. Don't worry, our team has been notified and we're working on a fix.": "От халепа! Щось пішло не так. Не хвилюйтеся, наша команда знає про це, ми вже працюємо над виправленням.",
|
||||
"Error Details:": "Деталі помилки:",
|
||||
"Try Again": "Спробувати ще раз",
|
||||
"Need help?": "Потрібна допомога?",
|
||||
"Contact Support": "Зв'язатися з підтримкою",
|
||||
"Code Highlighting": "Підсвічування коду",
|
||||
"Code Highlighting": "Підсвічувати код",
|
||||
"Enable Highlighting": "Увімкнути підсвічування",
|
||||
"Code Language": "Мова коду"
|
||||
"Code Language": "Мова коду",
|
||||
"Top Margin (px)": "Верхній відступ (px)",
|
||||
"Bottom Margin (px)": "Нижній відступ (px)",
|
||||
"Right Margin (px)": "Правий відступ (px)",
|
||||
"Left Margin (px)": "Лівий відступ (px)",
|
||||
"Column Gap (%)": "Проміжок між стовпцями (%)",
|
||||
"Always Show Status Bar": "Завжди показувати панель стану",
|
||||
"Translation Not Available": "Переклад недоступний",
|
||||
"Custom Content CSS": "Користуацький CSS вмісту",
|
||||
"Enter CSS for book content styling...": "Введіть CSS для стилізації вмісту книги...",
|
||||
"Custom Reader UI CSS": "CSS інтерфейсу",
|
||||
"Enter CSS for reader interface styling...": "Введіть CSS для стилізації інтерфейсу читанки...",
|
||||
"Crop": "Обрізати",
|
||||
"Book Covers": "Обкладинки книг",
|
||||
"Fit": "Припасувати",
|
||||
"Reset {{settings}}": "Скинути {{settings}}",
|
||||
"Reset Settings": "Скинути налаштування",
|
||||
"{{count}} pages left in chapter_one": "Залишилася {{count}} сторінка у розділі",
|
||||
"{{count}} pages left in chapter_few": "Залишилось {{count}} сторінки у розділі",
|
||||
"{{count}} pages left in chapter_many": "Залишилось {{count}} сторінок у розділі",
|
||||
"{{count}} pages left in chapter_other": "Залишилось {{count}} сторінок у розділі",
|
||||
"Show Remaining Pages": "Показати залишок сторінок",
|
||||
"Source Han Serif CN VF": "Source Han Serif",
|
||||
"Huiwen-mincho": "Huiwen Mincho",
|
||||
"KingHwa_OldSong": "KingHwa Song",
|
||||
"Manage Subscription": "Керувати підпискою",
|
||||
"Coming Soon": "Незабаром",
|
||||
"Upgrade to {{plan}}": "Перейти на {{plan}}",
|
||||
"Upgrade to Plus or Pro": "Перейти на Plus або Pro",
|
||||
"Current Plan": "Поточний план",
|
||||
"Plan Limits": "Обмеження плану",
|
||||
"Processing your payment...": "Обробка вашого платежу...",
|
||||
"Please wait while we confirm your subscription.": "Зачекайте, ми підтверджуємо вашу підписку.",
|
||||
"Payment Processing": "Обробка платежу",
|
||||
"Your payment is being processed. This usually takes a few moments.": "Ваш платіж обробляється. Зазвичай це займає кілька секунд.",
|
||||
"Payment Failed": "Платіж не вдався",
|
||||
"We couldn't process your subscription. Please try again or contact support if the issue persists.": "Не вдалося обробити вашу підписку. Спробуйте ще раз або зверніться до служби підтримки, якщо проблема не зникне.",
|
||||
"Back to Profile": "Повернутися до профілю",
|
||||
"Subscription Successful!": "Підписка активована!",
|
||||
"Thank you for your subscription! Your payment has been processed successfully.": "Дякуємо за підписку! Ваш платіж успішно оброблено.",
|
||||
"Email:": "Електронна пошта:",
|
||||
"Plan:": "План:",
|
||||
"Amount:": "Сума:",
|
||||
"Subscription ID:": "ID підписки:",
|
||||
"Go to Library": "Перейти до бібліотеки",
|
||||
"Need help? Contact our support team at support@readest.com": "Потрібна допомога? Зверніться до служби підтримки: support@readest.com",
|
||||
"Free Plan": "Безкоштовний план",
|
||||
"month": "місяць",
|
||||
"AI Translations (per day)": "ШІ-переклади (на день)",
|
||||
"Plus Plan": "План Plus",
|
||||
"Includes All Free Plan Benefits": "Включає всі переваги Безкоштовного плану",
|
||||
"Pro Plan": "План Pro",
|
||||
"More AI Translations": "Більше ШІ-перекладів",
|
||||
"Complete Your Subscription": "Завершіть підписку",
|
||||
"{{percentage}}% of Cloud Sync Space Used.": "Використано {{percentage}}% хмарного сховища.",
|
||||
"Cloud Sync Storage": "Хмарне сховище",
|
||||
"Disable": "Вимкнути",
|
||||
"Enable": "Увімкнути",
|
||||
"Upgrade to Readest Premium": "Оновити до Readest Premium",
|
||||
"Show Source Text": "Показати вихідний текст",
|
||||
"Cross-Platform Sync": "Міжплатформова синхронізація",
|
||||
"Seamlessly sync your library, progress, highlights, and notes across all your devices—never lose your place again.": "Безперешкодно синхронізуйте свою бібліотеку, прогрес, виділення та нотатки на всіх своїх пристроях. Більше ніколи не втратите свої книги.",
|
||||
"Customizable Reading": "Користувацьке читання",
|
||||
"Personalize every detail with adjustable fonts, layouts, themes, and advanced display settings for the perfect reading experience.": "Персоналізуйте кожну деталь за допомогою налаштовуваних шрифтів, макетів, тем та розширених налаштувань відображення для ідеального читання.",
|
||||
"AI Read Aloud": "Озвучування ШІ",
|
||||
"Enjoy hands-free reading with natural-sounding AI voices that bring your books to life.": "Насолоджуйтесь читанням без використання рук з природними звучаннями голосів, які оживляють ваші книги.",
|
||||
"AI Translations": "Переклади ШІ",
|
||||
"Translate any text instantly with the power of Google, Azure, or DeepL—understand content in any language.": "Перекладайте будь-який текст миттєво за допомогою Google, Azure або DeepL. Розумійте зміст будь-якою мовою.",
|
||||
"Connect with fellow readers and get help fast in our friendly community channels.": "Спілкуйтеся з іншими читачами та отримуйте швидку допомогу в наших дружніх каналах спільноти.",
|
||||
"Unlimited AI Read Aloud Hours": "Необмежені години озвучування ШІ",
|
||||
"Listen without limits—convert as much text as you like into immersive audio.": "Слухайте без обмежень—перетворюйте стільки тексту, скільки хочете, в захоплююче аудіо.",
|
||||
"Unlock enhanced translation capabilities with more daily usage and advanced options.": "Розблокуйте покращені можливості перекладу з більшим щоденним використанням та розширеними функціями.",
|
||||
"DeepL Pro Access": "Доступ до DeepL Pro",
|
||||
"Enjoy faster responses and dedicated assistance whenever you need help.": "Насолоджуйтеся швидшими відповідями та персональною допомогою, коли вам потрібна підтримка.",
|
||||
"Daily translation quota reached. Upgrade your plan to continue using AI translations.": "Досягнуто денну квоту перекладів. Оновіть свій план, щоб продовжити використовувати переклади ШІ.",
|
||||
"Includes All Plus Plan Benefits": "Включає усі переваги Plus плану",
|
||||
"Early Feature Access": "Ранній Доступ до функцій",
|
||||
"Be the first to explore new features, updates, and innovations before anyone else.": "Досліджуйте нові функції, оновлення та інновації першими.",
|
||||
"Advanced AI Tools": "Розширені ШІ інструменти",
|
||||
"Harness powerful AI tools for smarter reading, translation, and content discovery.": "Використовуйте потужні ШІ інструменти для розумного читання, перекладу та пошуку контенту.",
|
||||
"Translate up to 100,000 characters daily with the most accurate translation engine available.": "Перекладайте до 100 000 символів щодня з найточнішим доступним рушієм перекладу.",
|
||||
"Translate up to 500,000 characters daily with the most accurate translation engine available.": "Перекладайте до 500 000 символів щодня з найточнішим доступним рушієм перекладу.",
|
||||
"Securely store and access your entire reading collection with up to 5 GB of cloud storage.": "Безпечно зберігайте та отримуйте доступ до всієї колекції читання з до 5 ГБ хмарного сховища.",
|
||||
"Securely store and access your entire reading collection with up to 20 GB of cloud storage.": "Безпечно зберігайте та отримуйте доступ до всієї колекції читання з до 20 ГБ хмарного сховища.",
|
||||
"Deleted cloud backup of the book: {{title}}": "Видалено хмарну резервну копію книги: {{title}}",
|
||||
"Are you sure to delete the cloud backup of the selected book?": "Ви впевнені, що хочете видалити хмарну резервну копію вибраної книги?",
|
||||
"What's New in Readest": "Що нового в Readest",
|
||||
"Enter book title": "Введіть назву книги",
|
||||
"Subtitle": "Підзаголовок",
|
||||
"Enter book subtitle": "Введіть підзаголовок книги",
|
||||
"Enter author name": "Введіть ім'я автора",
|
||||
"Series": "Цикл",
|
||||
"Enter series name": "Введіть назву циклу",
|
||||
"Series Index": "Номер циклу",
|
||||
"Enter series index": "Введіть номер циклу",
|
||||
"Total in Series": "Всього у циклі",
|
||||
"Enter total books in series": "Введіть загальну кількість книг у циклі",
|
||||
"Enter publisher": "Введіть видавництвця",
|
||||
"Publication Date": "Дата видання",
|
||||
"Identifier": "Ідентифікатор",
|
||||
"Enter book description": "Введіть опис книги",
|
||||
"Change cover image": "Змінити зображення обкладинки",
|
||||
"Replace": "Замінити",
|
||||
"Unlock cover": "Розблокувати обкладинку",
|
||||
"Lock cover": "Заблокувати обкладинку",
|
||||
"Auto-Retrieve Metadata": "Автоматично підтягнути метадані",
|
||||
"Auto-Retrieve": "Автоматичне підтягування",
|
||||
"Unlock all fields": "Розблокувати всі поля",
|
||||
"Unlock All": "Розблокувати все",
|
||||
"Lock all fields": "Заблокувати всі поля",
|
||||
"Lock All": "Заблокувати все",
|
||||
"Reset": "Скинути",
|
||||
"Edit Metadata": "Редагувати метадані",
|
||||
"Locked": "Заблоковано",
|
||||
"Select Metadata Source": "Оберіть джерело метаданих",
|
||||
"Keep manual input": "Зберегти ручне введення",
|
||||
"Google Books": "Google Книги",
|
||||
"Open Library": "Open Library",
|
||||
"Fiction, Science, History": "Художня література, Наукова фантастика, Історія",
|
||||
"Select Cover Image": "Оберіть зображення обкладинки",
|
||||
"Open Book in New Window": "Відкрити книгу в новому вікні",
|
||||
"Voices for {{lang}}": "Голоси для {{lang}}",
|
||||
"Yandex Translate": "Yandex Перекладач (росія)",
|
||||
"YYYY or YYYY-MM-DD": "YYYY або YYYY-MM-DD",
|
||||
"Restore Purchase": "Відновити покупку",
|
||||
"No purchases found to restore.": "Не знайдено покупок для відновлення.",
|
||||
"Failed to restore purchases.": "Не вдалося відновити покупки.",
|
||||
"Failed to manage subscription.": "Не вдалося відредагувати підписку.",
|
||||
"Failed to load subscription plans.": "Не вдалося завантажити плани підписки.",
|
||||
"year": "рік",
|
||||
"Failed to create checkout session": "Не вдалося створити сесію оформлення замовлення",
|
||||
"Storage": "Сховище",
|
||||
"Terms of Service": "Умови обслуговування",
|
||||
"Privacy Policy": "Політика конфіденційності",
|
||||
"Disable Double Click": "Вимкнути подвійне натискання",
|
||||
"TTS not supported for this document": "TTS не підтримується в цьому документі",
|
||||
"Reset Password": "Скинути пароль",
|
||||
"Show Reading Progress": "Показати проґрес читання",
|
||||
"Reading Progress Style": "Стиль проґресу читання",
|
||||
"Page Number": "Номер сторінки",
|
||||
"Percentage": "Відсотки",
|
||||
"Deleted local copy of the book: {{title}}": "Видалено локальну копію книги: {{title}}",
|
||||
"Failed to delete cloud backup of the book: {{title}}": "Не вдалося видалити резервну копію книги в хмарі: {{title}}",
|
||||
"Failed to delete local copy of the book: {{title}}": "Не вдалося видалити локальну копію книги: {{title}}",
|
||||
"Are you sure to delete the local copy of the selected book?": "Ви впевнені, що хочете видалити локальну копію вибраної книги?",
|
||||
"Remove from Cloud & Device": "Видалити з хмари та пристрою",
|
||||
"Remove from Cloud Only": "Видалити тільки із хмари",
|
||||
"Remove from Device Only": "Видалити тільки із пристрою",
|
||||
"Disconnected": "Від'єднано",
|
||||
"KOReader Sync Settings": "Налаштування синхронізації KOReader",
|
||||
"Sync Strategy": "Стратегія синхронізації",
|
||||
"Ask on conflict": "Запитувати при конфлікті",
|
||||
"Always use latest": "Завжди використовувати найновішу",
|
||||
"Send changes only": "Відправити тільки зміни",
|
||||
"Receive changes only": "Отримати тільки зміни",
|
||||
"Disabled": "Вимкнено",
|
||||
"Checksum Method": "Метод контролю суми",
|
||||
"File Content (recommended)": "Вміст файлу (рекомендується)",
|
||||
"File Name": "Ім'я файлу",
|
||||
"Device Name": "Ім'я пристрою",
|
||||
"Sync Tolerance": "Допустима похибка синхронізації",
|
||||
"Connect to your KOReader Sync server.": "Підключіться до свого сервера синхронізації KOReader.",
|
||||
"Server URL": "URL сервера",
|
||||
"Username": "Ім'я користувача",
|
||||
"Your Username": "Ваше ім'я користувача",
|
||||
"Password": "Пароль",
|
||||
"Connect": "Під'єднатися",
|
||||
"KOReader Sync": "Синхронізація KOReader",
|
||||
"Sync Conflict": "Конфлікт синхронізації",
|
||||
"Sync reading progress from \"{{deviceName}}\"?": "Синхронізувати прогрес читання із \"{{deviceName}}\"?",
|
||||
"another device": "інший пристрій",
|
||||
"Local Progress": "Локальний прогрес",
|
||||
"Remote Progress": "Прогрес у хмарі",
|
||||
"Page {{page}} of {{total}} ({{percentage}}%)": "Сторінка {{page}} з {{total}} ({{percentage}}%)",
|
||||
"Current position": "Поточна позиція",
|
||||
"Approximately page {{page}} of {{total}} ({{percentage}}%)": "Приблизно сторінка {{page}} із {{total}} ({{percentage}}%)",
|
||||
"Approximately {{percentage}}%": "Приблизно {{percentage}}%",
|
||||
"Failed to connect": "Не вдалося під'єднатися",
|
||||
"Sync Server Connected": "Сервер синхронізації під'єднано",
|
||||
"Precision: {{precision}} digits after the decimal": "Точність: {{precision}} знаків після коми",
|
||||
"Sync as {{userDisplayName}}": "Синхронізувати як {{userDisplayName}}"
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
"Confirm Deletion": "Xác nhận xóa",
|
||||
"Copied to notebook": "Đã sao chép vào sổ ghi chú",
|
||||
"Copy": "Sao chép",
|
||||
"Custom CSS": "CSS tùy chỉnh",
|
||||
"Dark Mode": "Chế độ tối",
|
||||
"Default": "Mặc định",
|
||||
"Default Font": "Phông chữ mặc định",
|
||||
@@ -28,7 +27,6 @@
|
||||
"Dictionary": "Từ điển",
|
||||
"Download Readest": "Tải Readest",
|
||||
"Edit": "Chỉnh sửa",
|
||||
"Enter your custom CSS here...": "Nhập CSS tùy chỉnh của bạn vào đây...",
|
||||
"Excerpts": "Trích dẫn",
|
||||
"Failed to import book(s): {{filenames}}": "Không thể nhập sách: {{filenames}}",
|
||||
"Fast": "Nhanh",
|
||||
@@ -48,7 +46,6 @@
|
||||
"Horizontal Direction": "Hướng ngang",
|
||||
"Hyphenation": "Gạch nối từ",
|
||||
"Import Books": "Nhập sách",
|
||||
"Language:": "Ngôn ngữ:",
|
||||
"Layout": "Bố cục",
|
||||
"Light Mode": "Chế độ sáng",
|
||||
"Loading...": "Đang tải...",
|
||||
@@ -70,8 +67,8 @@
|
||||
"Paging Animation": "Hiệu ứng lật trang",
|
||||
"Paragraph": "Đoạn văn",
|
||||
"Parallel Read": "Đọc song song",
|
||||
"Published:": "Xuất bản:",
|
||||
"Publisher:": "Nhà xuất bản:",
|
||||
"Published": "Xuất bản",
|
||||
"Publisher": "Nhà xuất bản",
|
||||
"Reading Progress Synced": "Tiến độ đọc đã được đồng bộ",
|
||||
"Reload Page": "Tải lại trang",
|
||||
"Reveal in File Explorer": "Hiển thị trong File Explorer",
|
||||
@@ -96,16 +93,15 @@
|
||||
"Slow": "Chậm",
|
||||
"Solarized": "Solarized",
|
||||
"Speak": "Đọc",
|
||||
"Subjects:": "Chủ đề:",
|
||||
"Subjects": "Chủ đề",
|
||||
"System Fonts": "Phông chữ hệ thống",
|
||||
"TTS not supported in this device": "TTS không được hỗ trợ trên thiết bị này",
|
||||
"Theme Color": "Màu chủ đề",
|
||||
"Theme Mode": "Chế độ chủ đề",
|
||||
"Translate": "Dịch",
|
||||
"Translated Text": "Văn bản dịch",
|
||||
"Unknown": "Không xác định",
|
||||
"Untitled": "Không có tiêu đề",
|
||||
"Updated:": "Cập nhật:",
|
||||
"Updated": "Cập nhật",
|
||||
"Version {{version}}": "Phiên bản {{version}}",
|
||||
"Vertical Direction": "Hướng dọc",
|
||||
"Welcome to your library. You can import your books here and read them anytime.": "Chào mừng đến với thư viện của bạn. Bạn có thể nhập sách vào đây và đọc bất cứ lúc nào.",
|
||||
@@ -114,8 +110,6 @@
|
||||
"Your Library": "Thư viện của bạn",
|
||||
"TTS not supported for PDF": "TTS không được hỗ trợ cho PDF",
|
||||
"Override Book Font": "Ghi đè phông chữ sách",
|
||||
"Vertical Margins (px)": "Lề dọc (px)",
|
||||
"Horizontal Margins (%)": "Lề ngang (%)",
|
||||
"Apply to All Books": "Áp dụng cho tất cả sách",
|
||||
"Apply to This Book": "Áp dụng cho sách này",
|
||||
"Unable to fetch the translation. Try again later.": "Không thể tải dịch vụ. Vui lòng thử lại sau.",
|
||||
@@ -130,8 +124,6 @@
|
||||
"Failed to download book: {{title}}": "Không thể tải về sách: {{title}}",
|
||||
"Upload Book": "Tải lên sách",
|
||||
"Auto Upload Books to Cloud": "Tự động tải sách lên đám mây",
|
||||
"{{percentage}}% of Cloud Storage Used.": "{{percentage}}% dung lượng lưu trữ đám mây đã sử dụng.",
|
||||
"Storage": "Lưu trữ",
|
||||
"Book deleted: {{title}}": "Sách đã xóa: {{title}}",
|
||||
"Failed to delete book: {{title}}": "Không thể xóa sách: {{title}}",
|
||||
"Check Updates on Start": "Kiểm tra cập nhật khi khởi động",
|
||||
@@ -163,7 +155,7 @@
|
||||
"Create a Password": "Tạo mật khẩu",
|
||||
"Sign up": "Đăng ký",
|
||||
"Signing up...": "Đang đăng ký...",
|
||||
"Don’t have an account? Sign up": "Chưa có tài khoản? Đăng ký",
|
||||
"Don't have an account? Sign up": "Chưa có tài khoản? Đăng ký",
|
||||
"Check your email for the confirmation link": "Kiểm tra email của bạn để nhận liên kết xác nhận",
|
||||
"Signing in ...": "Đang đăng nhập ...",
|
||||
"Send a magic link email": "Gửi email liên kết ma thuật",
|
||||
@@ -187,35 +179,13 @@
|
||||
"Sign in with GitHub": "Đăng nhập với GitHub",
|
||||
"Account": "Tài khoản",
|
||||
"Failed to delete user. Please try again later.": "Không thể xóa người dùng. Vui lòng thử lại sau.",
|
||||
"Unlimited Offline/Online Reading": "Đọc ngoại tuyến/trực tuyến không giới hạn",
|
||||
"Unlimited Cloud Sync Devices": "Đồng bộ hóa thiết bị đám mây không giới hạn",
|
||||
"Essential Text-to-Speech Voices": "Giọng nói chuyển văn bản thành giọng nói cơ bản",
|
||||
"DeepL Free Access": "Truy cập DeepL miễn phí",
|
||||
"Community Support": "Hỗ trợ cộng đồng",
|
||||
"500 MB Cloud Sync Space": "500 MB không gian đồng bộ hóa đám mây",
|
||||
"Includes All Free Tier Benefits": "Bao gồm tất cả lợi ích của gói miễn phí",
|
||||
"AI Summaries": "Tóm tắt AI",
|
||||
"AI Translations": "Dịch thuật AI",
|
||||
"Priority Support": "Hỗ trợ ưu tiên",
|
||||
"DeepL Pro Access": "Truy cập DeepL Pro",
|
||||
"Expanded Text-to-Speech Voices": "Giọng nói chuyển văn bản thành giọng nói mở rộng",
|
||||
"2000 MB Cloud Sync Space": "2000 MB không gian đồng bộ hóa đám mây",
|
||||
"Includes All Plus Tier Benefits": "Bao gồm tất cả lợi ích của gói Plus",
|
||||
"Unlimited AI Summaries": "Tóm tắt AI không giới hạn",
|
||||
"Unlimited AI Translations": "Dịch thuật AI không giới hạn",
|
||||
"Unlimited DeepL Pro Access": "Truy cập DeepL Pro không giới hạn",
|
||||
"Advanced AI Tools": "Công cụ AI nâng cao",
|
||||
"Early Feature Access": "Truy cập tính năng sớm",
|
||||
"10 GB Cloud Sync Space": "10 GB không gian đồng bộ hóa đám mây",
|
||||
"Loading profile...": "Đang tải hồ sơ...",
|
||||
"Features": "Tính năng",
|
||||
"Delete Account": "Xóa tài khoản",
|
||||
"Delete Your Account?": "Xóa tài khoản của bạn?",
|
||||
"This action cannot be undone. All your data in the cloud will be permanently deleted.": "Hành động này không thể hoàn tác. Tất cả dữ liệu của bạn trên đám mây sẽ bị xóa vĩnh viễn.",
|
||||
"Delete Permanently": "Xóa vĩnh viễn",
|
||||
"Free Tier": "Gói miễn phí",
|
||||
"Plus Tier": "Gói Plus",
|
||||
"Pro Tier": "Gói Pro",
|
||||
"RTL Direction": "Hướng RTL",
|
||||
"Maximum Column Height": "Chiều cao cột tối đa",
|
||||
"Maximum Column Width": "Chiều rộng cột tối đa",
|
||||
@@ -266,13 +236,13 @@
|
||||
"Clear Search": "Xóa tìm kiếm",
|
||||
"Header & Footer": "Đầu trang & Chân trang",
|
||||
"Apply also in Scrolled Mode": "Áp dụng cũng trong chế độ cuộn",
|
||||
"A new version of Readest is Available!": "Đã có phiên bản mới của Readest!",
|
||||
"A new version of Readest is available!": "Đã có phiên bản mới của Readest!",
|
||||
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} đã có sẵn (phiên bản đã cài đặt {{currentVersion}}).",
|
||||
"Download and install now?": "Tải xuống và cài đặt ngay bây giờ?",
|
||||
"Downloading {{downloaded}} of {{contentLength}}": "Đang tải {{downloaded}} trong {{contentLength}}",
|
||||
"Download finished": "Tải xuống hoàn tất",
|
||||
"DOWNLOAD & INSTALL": "TẢI XUỐNG & CÀI ĐẶT",
|
||||
"Changelog:": "Thay đổi:",
|
||||
"Changelog": "Thay đổi",
|
||||
"Software Update": "Cập nhật phần mềm",
|
||||
"Title": "Tiêu đề",
|
||||
"Date Read": "Ngày đã đọc",
|
||||
@@ -281,10 +251,9 @@
|
||||
"Ascending": "Tăng dần",
|
||||
"Descending": "Giảm dần",
|
||||
"Sort by...": "Sắp xếp theo...",
|
||||
"Added:": "Đã thêm:",
|
||||
"Format:": "Định dạng:",
|
||||
"Added": "Đã thêm",
|
||||
"GuanKiapTsingKhai-T": "GuanKiapTsingKhai-T",
|
||||
"Description:": "Miêu tả:",
|
||||
"Description": "Miêu tả",
|
||||
"No description available": "Không có mô tả nào",
|
||||
"List": "Danh sách",
|
||||
"Grid": "Lưới",
|
||||
@@ -301,7 +270,7 @@
|
||||
"Checking for updates...": "Đang kiểm tra cập nhật...",
|
||||
"Error checking for updates": "Lỗi khi kiểm tra cập nhật",
|
||||
"Details": "Chi tiết",
|
||||
"File Size:": "Kích thước tệp:",
|
||||
"File Size": "Kích thước tệp",
|
||||
"Auto Detect": "Phát hiện tự động",
|
||||
"Next Section": "Phần tiếp theo",
|
||||
"Previous Section": "Phần trước",
|
||||
@@ -340,14 +309,12 @@
|
||||
"Synced at {{time}}": "Đã đồng bộ lúc {{time}}",
|
||||
"Never synced": "Chưa từng đồng bộ",
|
||||
"Show Remaining Time": "Hiển thị thời gian còn lại",
|
||||
"Show Page Number": "Hiển thị số trang",
|
||||
"{{time}} min left in chapter": "{{time}} phút còn lại trong chương",
|
||||
"Override Book Color": "Đổi màu sách",
|
||||
"Login Required": "Cần đăng nhập",
|
||||
"Quota Exceeded": "Vượt hạn mức",
|
||||
"{{percentage}}% of Daily Translation Characters Used.": "{{percentage}}% số ký tự dịch hàng ngày đã sử dụng.",
|
||||
"Translation Characters": "Những ký tự dịch",
|
||||
"Daily translation quota reached. Select another translate service to proceed.": "Đã đạt hạn mức dịch hàng ngày. Chọn dịch vụ dịch khác để tiếp tục.",
|
||||
"{{engine}}: {{count}} voices_other": "{{engine}}: {{count}} giọng",
|
||||
"Something went wrong. Don't worry, our team has been notified and we're working on a fix.": "Đã xảy ra sự cố. Đừng lo, nhóm của chúng tôi đã được thông báo và chúng tôi đang làm việc để sửa chữa.",
|
||||
"Error Details:": "Chi tiết lỗi:",
|
||||
@@ -356,5 +323,182 @@
|
||||
"Contact Support": "Liên hệ hỗ trợ",
|
||||
"Code Highlighting": "Đánh dấu mã",
|
||||
"Enable Highlighting": "Cho phép đánh dấu",
|
||||
"Code Language": "Ngôn ngữ mã"
|
||||
"Code Language": "Ngôn ngữ mã",
|
||||
"Top Margin (px)": "Lề trên (px)",
|
||||
"Bottom Margin (px)": "Lề dưới (px)",
|
||||
"Right Margin (px)": "Lề phải (px)",
|
||||
"Left Margin (px)": "Lề trái (px)",
|
||||
"Column Gap (%)": "Khoảng cách cột (%)",
|
||||
"Always Show Status Bar": "Luôn hiển thị thanh trạng thái",
|
||||
"Translation Not Available": "Không có bản dịch",
|
||||
"Custom Content CSS": "CSS nội dung",
|
||||
"Enter CSS for book content styling...": "Nhập CSS cho phần nội dung sách...",
|
||||
"Custom Reader UI CSS": "CSS giao diện đọc",
|
||||
"Enter CSS for reader interface styling...": "Nhập CSS cho giao diện trình đọc...",
|
||||
"Crop": "Cắt",
|
||||
"Book Covers": "Bìa sách",
|
||||
"Fit": "Vừa vặn",
|
||||
"Reset {{settings}}": "Đặt lại {{settings}}",
|
||||
"Reset Settings": "Đặt lại cài đặt",
|
||||
"{{count}} pages left in chapter_other": "Còn {{count}} trang trong chương",
|
||||
"Show Remaining Pages": "Hiển thị trang còn lại",
|
||||
"Source Han Serif CN VF": "Source Han Serif",
|
||||
"Huiwen-mincho": "Huiwen Mincho",
|
||||
"KingHwa_OldSong": "KingHwa Song",
|
||||
"Manage Subscription": "Quản lý đăng ký",
|
||||
"Coming Soon": "Sắp ra mắt",
|
||||
"Upgrade to {{plan}}": "Nâng cấp lên {{plan}}",
|
||||
"Upgrade to Plus or Pro": "Nâng cấp lên Plus hoặc Pro",
|
||||
"Current Plan": "Gói hiện tại",
|
||||
"Plan Limits": "Giới hạn gói",
|
||||
"Processing your payment...": "Đang xử lý thanh toán...",
|
||||
"Please wait while we confirm your subscription.": "Vui lòng chờ trong khi chúng tôi xác nhận đăng ký của bạn.",
|
||||
"Payment Processing": "Đang xử lý thanh toán",
|
||||
"Your payment is being processed. This usually takes a few moments.": "Thanh toán của bạn đang được xử lý. Thường mất vài giây.",
|
||||
"Payment Failed": "Thanh toán thất bại",
|
||||
"We couldn't process your subscription. Please try again or contact support if the issue persists.": "Chúng tôi không thể xử lý đăng ký của bạn. Vui lòng thử lại hoặc liên hệ hỗ trợ nếu sự cố tiếp tục.",
|
||||
"Back to Profile": "Quay lại hồ sơ",
|
||||
"Subscription Successful!": "Đăng ký thành công!",
|
||||
"Thank you for your subscription! Your payment has been processed successfully.": "Cảm ơn bạn đã đăng ký! Thanh toán của bạn đã được xử lý thành công.",
|
||||
"Email:": "Email:",
|
||||
"Plan:": "Gói:",
|
||||
"Amount:": "Số tiền:",
|
||||
"Subscription ID:": "ID đăng ký:",
|
||||
"Go to Library": "Đi đến Thư viện",
|
||||
"Need help? Contact our support team at support@readest.com": "Cần hỗ trợ? Liên hệ nhóm hỗ trợ tại support@readest.com",
|
||||
"Free Plan": "Gói miễn phí",
|
||||
"month": "tháng",
|
||||
"AI Translations (per day)": "Dịch AI (mỗi ngày)",
|
||||
"Plus Plan": "Gói Plus",
|
||||
"Includes All Free Plan Benefits": "Bao gồm tất cả lợi ích của gói miễn phí",
|
||||
"Pro Plan": "Gói Pro",
|
||||
"More AI Translations": "Thêm dịch AI",
|
||||
"Complete Your Subscription": "Hoàn tất đăng ký của bạn",
|
||||
"{{percentage}}% of Cloud Sync Space Used.": "Đã sử dụng {{percentage}}% không gian đồng bộ đám mây.",
|
||||
"Cloud Sync Storage": "Lưu trữ đồng bộ đám mây",
|
||||
"Disable": "Tắt",
|
||||
"Enable": "Bật",
|
||||
"Upgrade to Readest Premium": "Đăng ký Readest Premium",
|
||||
"Show Source Text": "Hiển thị văn bản gốc",
|
||||
"Cross-Platform Sync": "Đồng Bộ Đa Nền Tảng",
|
||||
"Seamlessly sync your library, progress, highlights, and notes across all your devices—never lose your place again.": "Đồng bộ liền mạch thư viện, tiến độ, đánh dấu và ghi chú trên tất cả thiết bị—không bao giờ mất vị trí đọc nữa.",
|
||||
"Customizable Reading": "Đọc Tùy Chỉnh",
|
||||
"Personalize every detail with adjustable fonts, layouts, themes, and advanced display settings for the perfect reading experience.": "Cá nhân hóa mọi chi tiết với phông chữ, bố cục, chủ đề có thể điều chỉnh và cài đặt hiển thị nâng cao cho trải nghiệm đọc hoàn hảo.",
|
||||
"AI Read Aloud": "AI Đọc To",
|
||||
"Enjoy hands-free reading with natural-sounding AI voices that bring your books to life.": "Thưởng thức việc đọc rảnh tay với giọng AI tự nhiên làm sống động sách của bạn.",
|
||||
"AI Translations": "Dịch Thuật AI",
|
||||
"Translate any text instantly with the power of Google, Azure, or DeepL—understand content in any language.": "Dịch bất kỳ văn bản nào ngay lập tức với sức mạnh của Google, Azure hoặc DeepL—hiểu nội dung bằng mọi ngôn ngữ.",
|
||||
"Connect with fellow readers and get help fast in our friendly community channels.": "Kết nối với độc giả khác và nhận trợ giúp nhanh chóng trong các kênh cộng đồng thân thiện.",
|
||||
"Unlimited AI Read Aloud Hours": "Giờ Đọc To AI Không Giới Hạn",
|
||||
"Listen without limits—convert as much text as you like into immersive audio.": "Nghe không giới hạn—chuyển đổi bao nhiêu văn bản bạn muốn thành âm thanh sống động.",
|
||||
"Unlock enhanced translation capabilities with more daily usage and advanced options.": "Mở khóa khả năng dịch thuật nâng cao với nhiều lượt sử dụng hàng ngày và tùy chọn nâng cao.",
|
||||
"DeepL Pro Access": "Truy Cập DeepL Pro",
|
||||
"Enjoy faster responses and dedicated assistance whenever you need help.": "Tận hưởng phản hồi nhanh hơn và hỗ trợ chuyên biệt khi bạn cần giúp đỡ.",
|
||||
"Daily translation quota reached. Upgrade your plan to continue using AI translations.": "Đã đạt hạn ngạch dịch thuật hàng ngày. Nâng cấp gói để tiếp tục sử dụng dịch thuật AI.",
|
||||
"Includes All Plus Plan Benefits": "Bao Gồm Tất Cả Lợi Ích Gói Plus",
|
||||
"Early Feature Access": "Truy Cập Tính Năng Sớm",
|
||||
"Be the first to explore new features, updates, and innovations before anyone else.": "Khám phá tính năng mới, cập nhật và đổi mới trước người khác.",
|
||||
"Advanced AI Tools": "Công Cụ AI Nâng Cao",
|
||||
"Harness powerful AI tools for smarter reading, translation, and content discovery.": "Khai thác công cụ AI mạnh mẽ cho đọc thông minh, dịch thuật và khám phá nội dung.",
|
||||
"Translate up to 100,000 characters daily with the most accurate translation engine available.": "Dịch tới 100.000 ký tự hàng ngày với công cụ dịch chính xác nhất hiện có.",
|
||||
"Translate up to 500,000 characters daily with the most accurate translation engine available.": "Dịch tới 500.000 ký tự hàng ngày với công cụ dịch chính xác nhất hiện có.",
|
||||
"Securely store and access your entire reading collection with up to 5 GB of cloud storage.": "Lưu trữ và truy cập an toàn toàn bộ bộ sưu tập đọc với tới 5 GB lưu trữ đám mây.",
|
||||
"Securely store and access your entire reading collection with up to 20 GB of cloud storage.": "Lưu trữ và truy cập an toàn toàn bộ bộ sưu tập đọc với tới 20 GB lưu trữ đám mây.",
|
||||
"Deleted cloud backup of the book: {{title}}": "Đã xóa bản sao lưu đám mây của sách: {{title}}",
|
||||
"Are you sure to delete the cloud backup of the selected book?": "Bạn có chắc chắn muốn xóa bản sao lưu đám mây của sách đã chọn không?",
|
||||
"What's New in Readest": "Những gì mới trong Readest",
|
||||
"Enter book title": "Nhập tiêu đề sách",
|
||||
"Subtitle": "Phụ đề",
|
||||
"Enter book subtitle": "Nhập phụ đề sách",
|
||||
"Enter author name": "Nhập tên tác giả",
|
||||
"Series": "Bộ sách",
|
||||
"Enter series name": "Nhập tên bộ sách",
|
||||
"Series Index": "Số thứ tự trong bộ",
|
||||
"Enter series index": "Nhập số thứ tự trong bộ",
|
||||
"Total in Series": "Tổng số trong bộ",
|
||||
"Enter total books in series": "Nhập tổng số sách trong bộ",
|
||||
"Enter publisher": "Nhập nhà xuất bản",
|
||||
"Publication Date": "Ngày xuất bản",
|
||||
"Identifier": "Định danh",
|
||||
"Enter book description": "Nhập mô tả sách",
|
||||
"Change cover image": "Thay đổi ảnh bìa",
|
||||
"Replace": "Thay thế",
|
||||
"Unlock cover": "Mở khóa bìa",
|
||||
"Lock cover": "Khóa bìa",
|
||||
"Auto-Retrieve Metadata": "Tự động lấy siêu dữ liệu",
|
||||
"Auto-Retrieve": "Tự động lấy",
|
||||
"Unlock all fields": "Mở khóa tất cả trường",
|
||||
"Unlock All": "Mở khóa tất cả",
|
||||
"Lock all fields": "Khóa tất cả trường",
|
||||
"Lock All": "Khóa tất cả",
|
||||
"Reset": "Đặt lại",
|
||||
"Edit Metadata": "Chỉnh sửa siêu dữ liệu",
|
||||
"Locked": "Đã khóa",
|
||||
"Select Metadata Source": "Chọn nguồn siêu dữ liệu",
|
||||
"Keep manual input": "Giữ nhập liệu thủ công",
|
||||
"Google Books": "Google Books",
|
||||
"Open Library": "Open Library",
|
||||
"Fiction, Science, History": "Tiểu thuyết, Khoa học, Lịch sử",
|
||||
"Select Cover Image": "Chọn ảnh bìa",
|
||||
"Open Book in New Window": "Mở Sách trong Cửa sổ Mới",
|
||||
"Voices for {{lang}}": "Giọng nói cho {{lang}}",
|
||||
"Yandex Translate": "Yandex Dịch",
|
||||
"YYYY or YYYY-MM-DD": "YYYY hoặc YYYY-MM-DD",
|
||||
"Restore Purchase": "Khôi phục mua hàng",
|
||||
"No purchases found to restore.": "Không tìm thấy giao dịch mua nào để khôi phục.",
|
||||
"Failed to restore purchases.": "Khôi phục giao dịch mua không thành công.",
|
||||
"Failed to manage subscription.": "Quản lý đăng ký không thành công.",
|
||||
"Failed to load subscription plans.": "Tải kế hoạch đăng ký không thành công.",
|
||||
"year": "năm",
|
||||
"Failed to create checkout session": "Không thể tạo phiên làm việc thanh toán",
|
||||
"Storage": "Lưu trữ",
|
||||
"Terms of Service": "Điều khoản dịch vụ",
|
||||
"Privacy Policy": "Chính sách bảo mật",
|
||||
"Disable Double Click": "Tắt Nhấp Đôi",
|
||||
"TTS not supported for this document": "TTS không được hỗ trợ cho tài liệu này",
|
||||
"Reset Password": "Đặt lại mật khẩu",
|
||||
"Show Reading Progress": "Hiển thị tiến độ đọc",
|
||||
"Reading Progress Style": "Phong cách tiến độ đọc",
|
||||
"Page Number": "Số trang",
|
||||
"Percentage": "Phần trăm",
|
||||
"Deleted local copy of the book: {{title}}": "Đã xóa bản sao cục bộ của sách: {{title}}",
|
||||
"Failed to delete cloud backup of the book: {{title}}": "Không thể xóa bản sao lưu đám mây của sách: {{title}}",
|
||||
"Failed to delete local copy of the book: {{title}}": "Không thể xóa bản sao cục bộ của sách: {{title}}",
|
||||
"Are you sure to delete the local copy of the selected book?": "Bạn có chắc chắn muốn xóa bản sao cục bộ của cuốn sách đã chọn không?",
|
||||
"Remove from Cloud & Device": "Xóa khỏi Đám mây & Thiết bị",
|
||||
"Remove from Cloud Only": "Xóa chỉ khỏi Đám mây",
|
||||
"Remove from Device Only": "Xóa chỉ khỏi Thiết bị",
|
||||
"Disconnected": "Mất kết nối",
|
||||
"KOReader Sync Settings": "Cài đặt đồng bộ KOReader",
|
||||
"Sync Strategy": "Chiến lược đồng bộ",
|
||||
"Ask on conflict": "Hỏi khi có xung đột",
|
||||
"Always use latest": "Luôn sử dụng phiên bản mới nhất",
|
||||
"Send changes only": "Chỉ gửi thay đổi",
|
||||
"Receive changes only": "Chỉ nhận thay đổi",
|
||||
"Disabled": "Đã tắt",
|
||||
"Checksum Method": "Phương pháp kiểm tra",
|
||||
"File Content (recommended)": "Nội dung tệp (được khuyến nghị)",
|
||||
"File Name": "Tên tệp",
|
||||
"Device Name": "Tên thiết bị",
|
||||
"Sync Tolerance": "Độ dung sai đồng bộ",
|
||||
"Connect to your KOReader Sync server.": "Kết nối với máy chủ đồng bộ KOReader của bạn.",
|
||||
"Server URL": "URL máy chủ",
|
||||
"Username": "Tên người dùng",
|
||||
"Your Username": "Tên người dùng của bạn",
|
||||
"Password": "Mật khẩu",
|
||||
"Connect": "Kết nối",
|
||||
"KOReader Sync": "Đồng bộ KOReader",
|
||||
"Sync Conflict": "Xung đột đồng bộ",
|
||||
"Sync reading progress from \"{{deviceName}}\"?": "Đồng bộ tiến độ đọc từ \"{{deviceName}}\"?",
|
||||
"another device": "thiết bị khác",
|
||||
"Local Progress": "Tiến độ cục bộ",
|
||||
"Remote Progress": "Tiến độ từ xa",
|
||||
"Page {{page}} of {{total}} ({{percentage}}%)": "Trang {{page}} của {{total}} ({{percentage}}%)",
|
||||
"Current position": "Vị trí hiện tại",
|
||||
"Approximately page {{page}} of {{total}} ({{percentage}}%)": "Khoảng trang {{page}} của {{total}} ({{percentage}}%)",
|
||||
"Approximately {{percentage}}%": "Khoảng {{percentage}}%",
|
||||
"Failed to connect": "Không thể kết nối",
|
||||
"Sync Server Connected": "Máy chủ đồng bộ đã kết nối",
|
||||
"Precision: {{precision}} digits after the decimal": "Độ chính xác: {{precision}} chữ số sau dấu thập phân",
|
||||
"Sync as {{userDisplayName}}": "Đồng bộ dưới tên {{userDisplayName}}"
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
"Confirm Deletion": "确认删除",
|
||||
"Copied to notebook": "已复制到笔记本",
|
||||
"Copy": "复制",
|
||||
"Custom CSS": "自定义 CSS",
|
||||
"Dark Mode": "深色主题",
|
||||
"Default": "默认",
|
||||
"Default Font": "默认字体",
|
||||
@@ -28,7 +27,6 @@
|
||||
"Dictionary": "词典",
|
||||
"Download Readest": "下载 Readest",
|
||||
"Edit": "编辑",
|
||||
"Enter your custom CSS here...": "在此处输入您的自定义 CSS...",
|
||||
"Excerpts": "摘录",
|
||||
"Failed to import book(s): {{filenames}}": "导入书籍失败:{{filenames}}",
|
||||
"Fast": "快",
|
||||
@@ -48,7 +46,6 @@
|
||||
"Horizontal Direction": "横排",
|
||||
"Hyphenation": "断字",
|
||||
"Import Books": "导入书籍",
|
||||
"Language:": "语言",
|
||||
"Layout": "布局",
|
||||
"Light Mode": "浅色主题",
|
||||
"Loading...": "加载中...",
|
||||
@@ -70,8 +67,8 @@
|
||||
"Paging Animation": "翻页动画",
|
||||
"Paragraph": "段落",
|
||||
"Parallel Read": "并排阅读",
|
||||
"Published:": "出版日期",
|
||||
"Publisher:": "出版商",
|
||||
"Published": "出版日期",
|
||||
"Publisher": "出版商",
|
||||
"Reading Progress Synced": "阅读进度已同步",
|
||||
"Reload Page": "重新加载页面",
|
||||
"Reveal in File Explorer": "在文件管理器中显示",
|
||||
@@ -96,16 +93,15 @@
|
||||
"Slow": "慢",
|
||||
"Solarized": "日晖",
|
||||
"Speak": "朗读",
|
||||
"Subjects:": "主题",
|
||||
"Subjects": "主题",
|
||||
"System Fonts": "系统字体",
|
||||
"TTS not supported in this device": "此设备不支持 TTS",
|
||||
"Theme Color": "主题颜色",
|
||||
"Theme Mode": "主题模式",
|
||||
"Translate": "翻译",
|
||||
"Translated Text": "译文",
|
||||
"Unknown": "未知",
|
||||
"Untitled": "无标题",
|
||||
"Updated:": "更新日期",
|
||||
"Updated": "更新日期",
|
||||
"Version {{version}}": "版本 {{version}}",
|
||||
"Vertical Direction": "竖排",
|
||||
"Welcome to your library. You can import your books here and read them anytime.": "书库空空如也,您可以导入您的书籍并随时阅读。",
|
||||
@@ -114,8 +110,6 @@
|
||||
"Your Library": "书库",
|
||||
"TTS not supported for PDF": "PDF 文档暂不支持 TTS",
|
||||
"Override Book Font": "覆盖书籍字体",
|
||||
"Vertical Margins (px)": "垂直边距(px)",
|
||||
"Horizontal Margins (%)": "水平边距(%)",
|
||||
"Apply to All Books": "应用于所有书籍",
|
||||
"Apply to This Book": "应用于此书籍",
|
||||
"Unable to fetch the translation. Try again later.": "无法获取翻译,请稍后重试。",
|
||||
@@ -130,8 +124,6 @@
|
||||
"Failed to download book: {{title}}": "下载书籍失败:{{title}}",
|
||||
"Upload Book": "上传书籍",
|
||||
"Auto Upload Books to Cloud": "自动上传书籍到云端",
|
||||
"{{percentage}}% of Cloud Storage Used.": "已使用 {{percentage}}% 云存储空间",
|
||||
"Storage": "存储空间",
|
||||
"Book deleted: {{title}}": "书籍已删除:{{title}}",
|
||||
"Failed to delete book: {{title}}": "删除书籍失败:{{title}}",
|
||||
"Check Updates on Start": "启动时检查更新",
|
||||
@@ -163,7 +155,7 @@
|
||||
"Create a Password": "创建密码",
|
||||
"Sign up": "注册",
|
||||
"Signing up...": "正在注册...",
|
||||
"Don’t have an account? Sign up": "没有账号?注册",
|
||||
"Don't have an account? Sign up": "没有账号?注册",
|
||||
"Check your email for the confirmation link": "检查您的电子邮件以获取确认链接",
|
||||
"Signing in ...": "正在登录...",
|
||||
"Send a magic link email": "发送魔法链接邮件",
|
||||
@@ -187,35 +179,13 @@
|
||||
"Sign in with GitHub": "使用 GitHub 登录",
|
||||
"Account": "账户",
|
||||
"Failed to delete user. Please try again later.": "删除用户失败。请稍后重试。",
|
||||
"Unlimited Offline/Online Reading": "无限离线和在线阅读",
|
||||
"Unlimited Cloud Sync Devices": "无限云同步设备个数",
|
||||
"Essential Text-to-Speech Voices": "基础 TTS 语音包",
|
||||
"DeepL Free Access": "免费版 DeepL 翻译",
|
||||
"Community Support": "社区支持",
|
||||
"500 MB Cloud Sync Space": "500 MB 云同步空间",
|
||||
"Includes All Free Tier Benefits": "包含所有免费版权益",
|
||||
"AI Summaries": "AI 摘要",
|
||||
"AI Translations": "AI 翻译",
|
||||
"Priority Support": "优先支持",
|
||||
"DeepL Pro Access": "专业版 DeepL 翻译",
|
||||
"Expanded Text-to-Speech Voices": "更多文本转语音声音",
|
||||
"2000 MB Cloud Sync Space": "2000 MB 云同步空间",
|
||||
"Includes All Plus Tier Benefits": "包含所有增强版权益",
|
||||
"Unlimited AI Summaries": "无限使用 AI 摘要",
|
||||
"Unlimited AI Translations": "无限使用 AI 翻译",
|
||||
"Unlimited DeepL Pro Access": "无限使用专业版 DeepL 翻译",
|
||||
"Advanced AI Tools": "高级 AI 工具",
|
||||
"Early Feature Access": "抢先体验新功能",
|
||||
"10 GB Cloud Sync Space": "10 GB 云同步空间",
|
||||
"Loading profile...": "加载个人资料中...",
|
||||
"Features": "功能特性",
|
||||
"Delete Account": "删除账户",
|
||||
"Delete Your Account?": "删除您的账户?",
|
||||
"This action cannot be undone. All your data in the cloud will be permanently deleted.": "此操作无法撤销。您在云端的所有数据将被永久删除。",
|
||||
"Delete Permanently": "永久删除",
|
||||
"Free Tier": "免费版",
|
||||
"Plus Tier": "增强版",
|
||||
"Pro Tier": "专业版",
|
||||
"RTL Direction": "从右向左",
|
||||
"Maximum Column Height": "最大列高",
|
||||
"Maximum Column Width": "最大列宽",
|
||||
@@ -266,13 +236,13 @@
|
||||
"Clear Search": "清除搜索",
|
||||
"Header & Footer": "页眉页脚",
|
||||
"Apply also in Scrolled Mode": "应用到滚动模式",
|
||||
"A new version of Readest is Available!": "有新版本的 Readest 可更新!",
|
||||
"A new version of Readest is available!": "有新版本的 Readest 可更新!",
|
||||
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} 可更新(已安装版本 {{currentVersion}})。",
|
||||
"Download and install now?": "立即下载并安装?",
|
||||
"Downloading {{downloaded}} of {{contentLength}}": "正在下载 {{downloaded}} / {{contentLength}}",
|
||||
"Download finished": "下载完成",
|
||||
"DOWNLOAD & INSTALL": "下载并安装",
|
||||
"Changelog:": "更新日志:",
|
||||
"Changelog": "更新日志",
|
||||
"Software Update": "软件更新",
|
||||
"Title": "书名",
|
||||
"Date Read": "阅读日期",
|
||||
@@ -281,10 +251,9 @@
|
||||
"Ascending": "升序",
|
||||
"Descending": "降序",
|
||||
"Sort by...": "排序方式...",
|
||||
"Added:": "添加日期",
|
||||
"Format:": "格式",
|
||||
"Added": "添加日期",
|
||||
"GuanKiapTsingKhai-T": "原俠正楷-T",
|
||||
"Description:": "简介",
|
||||
"Description": "简介",
|
||||
"No description available": "暂无简介",
|
||||
"List": "列表",
|
||||
"Grid": "网格",
|
||||
@@ -301,7 +270,7 @@
|
||||
"Checking for updates...": "检查更新中...",
|
||||
"Error checking for updates": "检查更新时出错",
|
||||
"Details": "详情",
|
||||
"File Size:": "文件大小",
|
||||
"File Size": "文件大小",
|
||||
"Auto Detect": "自动检测",
|
||||
"Next Section": "下一章",
|
||||
"Previous Section": "上一章",
|
||||
@@ -340,15 +309,13 @@
|
||||
"Synced at {{time}}": "同步于:{{time}}",
|
||||
"Never synced": "从未同步",
|
||||
"Show Remaining Time": "显示剩余时间",
|
||||
"Show Page Number": "显示页码",
|
||||
"{{time}} min left in chapter": "本章剩余 {{time}} 分钟",
|
||||
"Override Book Color": "覆盖书籍颜色",
|
||||
"Login Required": "需登录",
|
||||
"Quota Exceeded": "额度不足",
|
||||
"{{percentage}}% of Daily Translation Characters Used.": "已使用 {{percentage}}% 的每日翻译字符数",
|
||||
"Translation Characters": "翻译字数",
|
||||
"Daily translation quota reached. Select another translate service to proceed.": "已达到每日翻译配额,请选择其他翻译服务继续。",
|
||||
"{{engine}}: {{count}} voices_other": "{{engine}}: {{count}} 种声音",
|
||||
"{{engine}}: {{count}} voices_other": "{{engine}}: {{count}} 种语音",
|
||||
"Something went wrong. Don't worry, our team has been notified and we're working on a fix.": "出了点问题。请不要担心,我们的团队已经收到通知并正在修复中。",
|
||||
"Error Details:": "错误详情:",
|
||||
"Try Again": "重试",
|
||||
@@ -356,5 +323,182 @@
|
||||
"Contact Support": "联系客服",
|
||||
"Code Highlighting": "代码高亮",
|
||||
"Enable Highlighting": "启用高亮",
|
||||
"Code Language": "代码语言"
|
||||
"Code Language": "代码语言",
|
||||
"Top Margin (px)": "上边距",
|
||||
"Bottom Margin (px)": "下边距",
|
||||
"Right Margin (px)": "右边距",
|
||||
"Left Margin (px)": "左边距",
|
||||
"Column Gap (%)": "列间距",
|
||||
"Always Show Status Bar": "始终显示状态栏",
|
||||
"Translation Not Available": "翻译不可用",
|
||||
"Custom Content CSS": "自定义内容 CSS",
|
||||
"Enter CSS for book content styling...": "输入书籍内容样式的CSS…",
|
||||
"Custom Reader UI CSS": "自定义界面 CSS",
|
||||
"Enter CSS for reader interface styling...": "输入阅读器界面样式的CSS…",
|
||||
"Crop": "裁剪",
|
||||
"Book Covers": "书籍封面",
|
||||
"Fit": "适应",
|
||||
"Reset {{settings}}": "重置{{settings}}",
|
||||
"Reset Settings": "重置设置",
|
||||
"{{count}} pages left in chapter_other": "本章剩余 {{count}} 页",
|
||||
"Show Remaining Pages": "显示剩余页数",
|
||||
"Source Han Serif CN VF": "思源宋体",
|
||||
"Huiwen-mincho": "汇文明朝体",
|
||||
"KingHwa_OldSong": "京华老宋体",
|
||||
"Manage Subscription": "管理订阅",
|
||||
"Coming Soon": "即将上线",
|
||||
"Upgrade to {{plan}}": "升级到 {{plan}}",
|
||||
"Upgrade to Plus or Pro": "升级到 Plus 或 Pro",
|
||||
"Current Plan": "当前套餐",
|
||||
"Plan Limits": "套餐限制",
|
||||
"Processing your payment...": "正在处理付款...",
|
||||
"Please wait while we confirm your subscription.": "请稍候,我们正在确认您的订阅。",
|
||||
"Payment Processing": "付款处理中",
|
||||
"Your payment is being processed. This usually takes a few moments.": "您的付款正在处理中,通常需要几秒钟。",
|
||||
"Payment Failed": "付款失败",
|
||||
"We couldn't process your subscription. Please try again or contact support if the issue persists.": "我们无法处理您的订阅。请重试,或如问题持续请联系支持。",
|
||||
"Back to Profile": "返回个人资料",
|
||||
"Subscription Successful!": "订阅成功!",
|
||||
"Thank you for your subscription! Your payment has been processed successfully.": "感谢您的订阅!您的付款已成功处理。",
|
||||
"Email:": "邮箱:",
|
||||
"Plan:": "套餐:",
|
||||
"Amount:": "金额:",
|
||||
"Subscription ID:": "订阅 ID:",
|
||||
"Go to Library": "前往书库",
|
||||
"Need help? Contact our support team at support@readest.com": "需要帮助?请联系支持团队:support@readest.com",
|
||||
"Free Plan": "免费套餐",
|
||||
"month": "月",
|
||||
"AI Translations (per day)": "AI 翻译(每日)",
|
||||
"Plus Plan": "Plus 套餐",
|
||||
"Includes All Free Plan Benefits": "包含所有免费套餐功能",
|
||||
"Pro Plan": "Pro 套餐",
|
||||
"More AI Translations": "更多 AI 翻译",
|
||||
"Complete Your Subscription": "完成您的订阅",
|
||||
"{{percentage}}% of Cloud Sync Space Used.": "已使用 {{percentage}}% 的云同步空间。",
|
||||
"Cloud Sync Storage": "云同步空间",
|
||||
"Disable": "禁用",
|
||||
"Enable": "启用",
|
||||
"Upgrade to Readest Premium": "升级到 Readest Premium",
|
||||
"Show Source Text": "显示原文",
|
||||
"Cross-Platform Sync": "跨平台同步",
|
||||
"Seamlessly sync your library, progress, highlights, and notes across all your devices—never lose your place again.": "在所有设备间无缝同步您的书库、进度、标注和笔记——再也不会丢失阅读位置。",
|
||||
"Customizable Reading": "个性化阅读",
|
||||
"Personalize every detail with adjustable fonts, layouts, themes, and advanced display settings for the perfect reading experience.": "通过可调节的字体、布局、主题和高级显示设置个性化每个细节,获得完美阅读体验。",
|
||||
"AI Read Aloud": "AI 朗读",
|
||||
"Enjoy hands-free reading with natural-sounding AI voices that bring your books to life.": "享受解放双手的阅读体验,使用自然流畅的 AI 语音让书籍栩栩如生。",
|
||||
"AI Translations": "AI 翻译",
|
||||
"Translate any text instantly with the power of Google, Azure, or DeepL—understand content in any language.": "借助 Google、Azure 或 DeepL 的强大功能即时翻译任何语言的内容。",
|
||||
"Connect with fellow readers and get help fast in our friendly community channels.": "与其他读者交流,在我们友好的社区频道中快速获得帮助。",
|
||||
"Unlimited AI Read Aloud Hours": "无限 AI 朗读时长",
|
||||
"Listen without limits—convert as much text as you like into immersive audio.": "将任意数量的文本转换为沉浸式音频。",
|
||||
"Unlock enhanced translation capabilities with more daily usage and advanced options.": "增强翻译功能、更多翻译用量和高级选项。",
|
||||
"DeepL Pro Access": "DeepL Pro 访问权限",
|
||||
"Enjoy faster responses and dedicated assistance whenever you need help.": "享受更快响应和专属帮助,随时获得支持。",
|
||||
"Daily translation quota reached. Upgrade your plan to continue using AI translations.": "已达每日翻译配额。升级套餐以继续使用 AI 翻译。",
|
||||
"Includes All Plus Plan Benefits": "包含所有 Plus 套餐功能",
|
||||
"Early Feature Access": "抢先体验新功能",
|
||||
"Be the first to explore new features, updates, and innovations before anyone else.": "抢先体验新功能、更新和创新。",
|
||||
"Advanced AI Tools": "高级 AI 工具",
|
||||
"Harness powerful AI tools for smarter reading, translation, and content discovery.": "利用强大的 AI 工具实现智能阅读、翻译和内容发现。",
|
||||
"Translate up to 100,000 characters daily with the most accurate translation engine available.": "使用最精准的翻译引擎每天可翻译10万字。",
|
||||
"Translate up to 500,000 characters daily with the most accurate translation engine available.": "使用最精准的翻译引擎每天可翻译50万字。",
|
||||
"Securely store and access your entire reading collection with up to 5 GB of cloud storage.": "使用多达 5GB 的安全云存储保存和访问您的整个阅读收藏。",
|
||||
"Securely store and access your entire reading collection with up to 20 GB of cloud storage.": "使用多达 20GB 的安全云存储保存和访问您的整个阅读收藏。",
|
||||
"Deleted cloud backup of the book: {{title}}": "已删除书籍云备份:{{title}}",
|
||||
"Are you sure to delete the cloud backup of the selected book?": "您确定要删除所选书籍的云备份吗?",
|
||||
"What's New in Readest": "Readest 更新功能",
|
||||
"Enter book title": "输入书名",
|
||||
"Subtitle": "副标题",
|
||||
"Enter book subtitle": "输入书籍副标题",
|
||||
"Enter author name": "输入作者姓名",
|
||||
"Series": "系列",
|
||||
"Enter series name": "输入系列名称",
|
||||
"Series Index": "系列编号",
|
||||
"Enter series index": "输入系列编号",
|
||||
"Total in Series": "系列总数",
|
||||
"Enter total books in series": "输入系列中书籍总数",
|
||||
"Enter publisher": "输入出版社",
|
||||
"Publication Date": "出版日期",
|
||||
"Identifier": "标识符",
|
||||
"Enter book description": "输入书籍描述",
|
||||
"Change cover image": "替换封面图片",
|
||||
"Replace": "替换",
|
||||
"Unlock cover": "解锁封面",
|
||||
"Lock cover": "锁定封面",
|
||||
"Auto-Retrieve Metadata": "自动获取元数据",
|
||||
"Auto-Retrieve": "自动获取",
|
||||
"Unlock all fields": "解锁所有字段",
|
||||
"Unlock All": "全部解锁",
|
||||
"Lock all fields": "锁定所有字段",
|
||||
"Lock All": "全部锁定",
|
||||
"Reset": "重置",
|
||||
"Edit Metadata": "编辑元数据",
|
||||
"Locked": "已锁定",
|
||||
"Select Metadata Source": "选择元数据来源",
|
||||
"Keep manual input": "保留手动输入",
|
||||
"Google Books": "谷歌图书",
|
||||
"Open Library": "开放图书馆",
|
||||
"Fiction, Science, History": "小说,科学,历史",
|
||||
"Select Cover Image": "选择封面图片",
|
||||
"Open Book in New Window": "在新窗口中打开书籍",
|
||||
"Voices for {{lang}}": "{{lang}} 的语音",
|
||||
"Yandex Translate": "Yandex 翻译",
|
||||
"YYYY or YYYY-MM-DD": "YYYY 或 YYYY-MM-DD",
|
||||
"Restore Purchase": "恢复购买",
|
||||
"No purchases found to restore.": "未找到可恢复的购买。",
|
||||
"Failed to restore purchases.": "恢复购买失败。",
|
||||
"Failed to manage subscription.": "管理订阅失败。",
|
||||
"Failed to load subscription plans.": "加载订阅计划失败。",
|
||||
"year": "年",
|
||||
"Failed to create checkout session": "创建结账会话失败",
|
||||
"Storage": "云同步空间",
|
||||
"Terms of Service": "服务条款",
|
||||
"Privacy Policy": "隐私政策",
|
||||
"Disable Double Click": "禁用双击",
|
||||
"TTS not supported for this document": "TTS 不支持此文档",
|
||||
"Reset Password": "重置密码",
|
||||
"Show Reading Progress": "显示阅读进度",
|
||||
"Reading Progress Style": "阅读进度样式",
|
||||
"Page Number": "页码",
|
||||
"Percentage": "百分比",
|
||||
"Deleted local copy of the book: {{title}}": "已删除书籍本地副本:{{title}}",
|
||||
"Failed to delete cloud backup of the book: {{title}}": "未能删除书籍云备份:{{title}}",
|
||||
"Failed to delete local copy of the book: {{title}}": "未能删除书籍本地副本:{{title}}",
|
||||
"Are you sure to delete the local copy of the selected book?": "您确定要删除所选书籍的本地副本吗?",
|
||||
"Remove from Cloud & Device": "从云端和设备中移除",
|
||||
"Remove from Cloud Only": "仅从云端移除",
|
||||
"Remove from Device Only": "仅从设备中移除",
|
||||
"Disconnected": "已断开连接",
|
||||
"KOReader Sync Settings": "KOReader 同步设置",
|
||||
"Sync Strategy": "同步策略",
|
||||
"Ask on conflict": "发生冲突时询问",
|
||||
"Always use latest": "始终使用最新",
|
||||
"Send changes only": "仅发送更改",
|
||||
"Receive changes only": "仅接收更改",
|
||||
"Disabled": "已禁用",
|
||||
"Checksum Method": "校验和方法",
|
||||
"File Content (recommended)": "文件内容(推荐)",
|
||||
"File Name": "文件名",
|
||||
"Device Name": "设备名称",
|
||||
"Sync Tolerance": "同步容忍度",
|
||||
"Connect to your KOReader Sync server.": "连接到您的 KOReader 同步服务器。",
|
||||
"Server URL": "服务器 URL",
|
||||
"Username": "用户名",
|
||||
"Your Username": "您的用户名",
|
||||
"Password": "密码",
|
||||
"Connect": "连接",
|
||||
"KOReader Sync": "KOReader 同步",
|
||||
"Sync Conflict": "同步冲突",
|
||||
"Sync reading progress from \"{{deviceName}}\"?": "从 \"{{deviceName}}\" 同步阅读进度?",
|
||||
"another device": "另一台设备",
|
||||
"Local Progress": "本地进度",
|
||||
"Remote Progress": "远程进度",
|
||||
"Page {{page}} of {{total}} ({{percentage}}%)": "第 {{page}} 页,共 {{total}} 页 ({{percentage}}%)",
|
||||
"Current position": "当前位置",
|
||||
"Approximately page {{page}} of {{total}} ({{percentage}}%)": "大约第 {{page}} 页,共 {{total}} 页 ({{percentage}}%)",
|
||||
"Approximately {{percentage}}%": "大约 {{percentage}}%",
|
||||
"Failed to connect": "连接失败",
|
||||
"Sync Server Connected": "同步服务器已连接",
|
||||
"Precision: {{precision}} digits after the decimal": "精度:{{precision}} 位小数",
|
||||
"Sync as {{userDisplayName}}": "以 {{userDisplayName}} 身份同步"
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
"Confirm Deletion": "確認刪除",
|
||||
"Copied to notebook": "已複製到筆記本",
|
||||
"Copy": "複製",
|
||||
"Custom CSS": "自定義 CSS",
|
||||
"Dark Mode": "深色主題",
|
||||
"Default": "預設",
|
||||
"Default Font": "預設字體",
|
||||
@@ -28,7 +27,6 @@
|
||||
"Dictionary": "詞典",
|
||||
"Download Readest": "下載 Readest",
|
||||
"Edit": "編輯",
|
||||
"Enter your custom CSS here...": "在此處輸入您的自定義 CSS...",
|
||||
"Excerpts": "摘錄",
|
||||
"Failed to import book(s): {{filenames}}": "導入書籍失敗:{{filenames}}",
|
||||
"Fast": "快",
|
||||
@@ -48,7 +46,6 @@
|
||||
"Horizontal Direction": "橫排",
|
||||
"Hyphenation": "斷字",
|
||||
"Import Books": "導入書籍",
|
||||
"Language:": "語言",
|
||||
"Layout": "版面",
|
||||
"Light Mode": "淺色主題",
|
||||
"Loading...": "載入中...",
|
||||
@@ -70,8 +67,8 @@
|
||||
"Paging Animation": "翻頁動畫",
|
||||
"Paragraph": "段落",
|
||||
"Parallel Read": "並排閱讀",
|
||||
"Published:": "出版日期",
|
||||
"Publisher:": "出版商",
|
||||
"Published": "出版日期",
|
||||
"Publisher": "出版商",
|
||||
"Reading Progress Synced": "閱讀進度已同步",
|
||||
"Reload Page": "重新載入頁面",
|
||||
"Reveal in File Explorer": "在檔案管理器中顯示",
|
||||
@@ -96,16 +93,15 @@
|
||||
"Slow": "慢",
|
||||
"Solarized": "日暉",
|
||||
"Speak": "朗讀",
|
||||
"Subjects:": "主題",
|
||||
"Subjects": "主題",
|
||||
"System Fonts": "系統字體",
|
||||
"TTS not supported in this device": "此設備不支持 TTS",
|
||||
"Theme Color": "主題顏色",
|
||||
"Theme Mode": "主題模式",
|
||||
"Translate": "翻譯",
|
||||
"Translated Text": "譯文",
|
||||
"Unknown": "未知",
|
||||
"Untitled": "無標題",
|
||||
"Updated:": "更新日期",
|
||||
"Updated": "更新日期",
|
||||
"Version {{version}}": "版本 {{version}}",
|
||||
"Vertical Direction": "豎排",
|
||||
"Welcome to your library. You can import your books here and read them anytime.": "書庫空空如也,您可以導入您的書籍並隨時閱讀。",
|
||||
@@ -114,8 +110,6 @@
|
||||
"Your Library": "書庫",
|
||||
"TTS not supported for PDF": "PDF 文檔暫不支持 TTS",
|
||||
"Override Book Font": "覆蓋書籍字體",
|
||||
"Vertical Margins (px)": "垂直邊距(px)",
|
||||
"Horizontal Margins (%)": "水平邊距(%)",
|
||||
"Apply to All Books": "應用於所有書籍",
|
||||
"Apply to This Book": "應用於此書籍",
|
||||
"Unable to fetch the translation. Try again later.": "無法獲取翻譯,請稍後再試。",
|
||||
@@ -130,8 +124,6 @@
|
||||
"Failed to download book: {{title}}": "書籍下載失敗:{{title}}",
|
||||
"Upload Book": "上傳書籍",
|
||||
"Auto Upload Books to Cloud": "自動上傳書籍到雲端",
|
||||
"{{percentage}}% of Cloud Storage Used.": "已使用 {{percentage}}% 雲存儲空間",
|
||||
"Storage": "存儲空間",
|
||||
"Book deleted: {{title}}": "書籍已刪除:{{title}}",
|
||||
"Failed to delete book: {{title}}": "書籍刪除失敗:{{title}}",
|
||||
"Check Updates on Start": "啟動時檢查更新",
|
||||
@@ -163,7 +155,7 @@
|
||||
"Create a Password": "創建密碼",
|
||||
"Sign up": "註冊",
|
||||
"Signing up...": "正在註冊...",
|
||||
"Don’t have an account? Sign up": "沒有帳號?註冊",
|
||||
"Don't have an account? Sign up": "沒有帳號?註冊",
|
||||
"Check your email for the confirmation link": "檢查您的電子郵件以獲取確認連結",
|
||||
"Signing in ...": "正在登入...",
|
||||
"Send a magic link email": "發送魔法連結郵件",
|
||||
@@ -187,35 +179,13 @@
|
||||
"Sign in with GitHub": "使用 GitHub 登入",
|
||||
"Account": "帳戶",
|
||||
"Failed to delete user. Please try again later.": "刪除使用者失敗。請稍後再試。",
|
||||
"Unlimited Offline/Online Reading": "無限離線/線上閱讀",
|
||||
"Unlimited Cloud Sync Devices": "無限雲端同步裝置",
|
||||
"Essential Text-to-Speech Voices": "基本文字轉語音聲音",
|
||||
"DeepL Free Access": "DeepL免費存取",
|
||||
"Community Support": "社群支援",
|
||||
"500 MB Cloud Sync Space": "500 MB雲端同步空間",
|
||||
"Includes All Free Tier Benefits": "包含所有免費方案權益",
|
||||
"AI Summaries": "AI摘要",
|
||||
"AI Translations": "AI翻譯",
|
||||
"Priority Support": "優先支援",
|
||||
"DeepL Pro Access": "DeepL專業版存取",
|
||||
"Expanded Text-to-Speech Voices": "擴展文字轉語音聲音",
|
||||
"2000 MB Cloud Sync Space": "2000 MB雲端同步空間",
|
||||
"Includes All Plus Tier Benefits": "包含所有Plus方案權益",
|
||||
"Unlimited AI Summaries": "無限AI摘要",
|
||||
"Unlimited AI Translations": "無限AI翻譯",
|
||||
"Unlimited DeepL Pro Access": "無限DeepL專業版存取",
|
||||
"Advanced AI Tools": "進階AI工具",
|
||||
"Early Feature Access": "搶先體驗新功能",
|
||||
"10 GB Cloud Sync Space": "10 GB雲端同步空間",
|
||||
"Loading profile...": "載入個人資料中...",
|
||||
"Features": "功能",
|
||||
"Delete Account": "刪除帳戶",
|
||||
"Delete Your Account?": "刪除您的帳戶?",
|
||||
"This action cannot be undone. All your data in the cloud will be permanently deleted.": "此操作無法撤銷。您在雲端的所有資料將被永久刪除。",
|
||||
"Delete Permanently": "永久刪除",
|
||||
"Free Tier": "免費方案",
|
||||
"Plus Tier": "Plus方案",
|
||||
"Pro Tier": "專業方案",
|
||||
"RTL Direction": "從右向左",
|
||||
"Maximum Column Height": "最大列高",
|
||||
"Maximum Column Width": "最大列寬",
|
||||
@@ -266,13 +236,13 @@
|
||||
"Clear Search": "清除搜索",
|
||||
"Header & Footer": "頁眉頁腳",
|
||||
"Apply also in Scrolled Mode": "滾動模式同樣適用",
|
||||
"A new version of Readest is Available!": "有新版本的 Readest 可更新!",
|
||||
"A new version of Readest is available!": "有新版本的 Readest 可更新!",
|
||||
"Readest {{newVersion}} is available (installed version {{currentVersion}}).": "Readest {{newVersion}} 可更新(已安裝版本 {{currentVersion}})。",
|
||||
"Download and install now?": "立即下載並安裝?",
|
||||
"Downloading {{downloaded}} of {{contentLength}}": "正在下載 {{downloaded}} / {{contentLength}}",
|
||||
"Download finished": "下載完成",
|
||||
"DOWNLOAD & INSTALL": "下載並安裝",
|
||||
"Changelog:": "更新日誌:",
|
||||
"Changelog": "更新日誌:",
|
||||
"Software Update": "軟體更新",
|
||||
"Title": "書名",
|
||||
"Date Read": "閱讀日期",
|
||||
@@ -281,10 +251,9 @@
|
||||
"Ascending": "升序",
|
||||
"Descending": "降序",
|
||||
"Sort by...": "排序方式...",
|
||||
"Added:": "添加日期",
|
||||
"Format:": "格式",
|
||||
"Added": "添加日期",
|
||||
"GuanKiapTsingKhai-T": "原俠正楷-T",
|
||||
"Description:": "簡介",
|
||||
"Description": "簡介",
|
||||
"No description available": "暫無簡介",
|
||||
"List": "列表",
|
||||
"Grid": "網格",
|
||||
@@ -301,7 +270,7 @@
|
||||
"Checking for updates...": "檢查更新中...",
|
||||
"Error checking for updates": "檢查更新時出錯",
|
||||
"Details": "詳情",
|
||||
"File Size:": "檔案大小",
|
||||
"File Size": "檔案大小",
|
||||
"Auto Detect": "自動檢測",
|
||||
"Next Section": "下一章",
|
||||
"Previous Section": "上一章",
|
||||
@@ -340,14 +309,12 @@
|
||||
"Synced at {{time}}": "同步於:{{time}}",
|
||||
"Never synced": "從未同步",
|
||||
"Show Remaining Time": "顯示剩餘時間",
|
||||
"Show Page Number": "顯示頁碼",
|
||||
"{{time}} min left in chapter": "本章剩餘 {{time}} 分鐘",
|
||||
"Override Book Color": "覆蓋書籍顏色",
|
||||
"Login Required": "需登入",
|
||||
"Quota Exceeded": "額度不足",
|
||||
"{{percentage}}% of Daily Translation Characters Used.": "已使用 {{percentage}}% 的每日翻譯字數",
|
||||
"Translation Characters": "翻譯字數",
|
||||
"Daily translation quota reached. Select another translate service to proceed.": "已達到每日翻譯字數限制,請選擇其他翻譯服務繼續。",
|
||||
"{{engine}}: {{count}} voices_other": "{{engine}}: {{count}} 種語音",
|
||||
"Something went wrong. Don't worry, our team has been notified and we're working on a fix.": "出了點問題。別擔心,我們的團隊已經收到通知,正在努力修復。",
|
||||
"Error Details:": "錯誤詳情:",
|
||||
@@ -356,5 +323,182 @@
|
||||
"Contact Support": "聯繫支援",
|
||||
"Code Highlighting": "程式碼高亮",
|
||||
"Enable Highlighting": "啟用高亮",
|
||||
"Code Language": "程式語言"
|
||||
"Code Language": "程式語言",
|
||||
"Top Margin (px)": "上邊距",
|
||||
"Bottom Margin (px)": "下邊距",
|
||||
"Right Margin (px)": "右邊距",
|
||||
"Left Margin (px)": "左邊距",
|
||||
"Column Gap (%)": "欄間距",
|
||||
"Always Show Status Bar": "始終顯示狀態欄",
|
||||
"Translation Not Available": "翻譯不可用",
|
||||
"Custom Content CSS": "自訂內容 CSS",
|
||||
"Enter CSS for book content styling...": "輸入書籍內容樣式的CSS…",
|
||||
"Custom Reader UI CSS": "自訂介面 CSS",
|
||||
"Enter CSS for reader interface styling...": "輸入閱讀器介面樣式的CSS…",
|
||||
"Crop": "裁剪",
|
||||
"Book Covers": "書籍封面",
|
||||
"Fit": "適應",
|
||||
"Reset {{settings}}": "重置{{settings}}",
|
||||
"Reset Settings": "重置設置",
|
||||
"{{count}} pages left in chapter_other": "本章剩餘 {{count}} 頁",
|
||||
"Show Remaining Pages": "顯示剩餘頁數",
|
||||
"Source Han Serif CN VF": "思源宋體",
|
||||
"Huiwen-mincho": "匯文明朝體",
|
||||
"KingHwa_OldSong": "京華老宋體",
|
||||
"Manage Subscription": "管理訂閱",
|
||||
"Coming Soon": "即將推出",
|
||||
"Upgrade to {{plan}}": "升級到 {{plan}}",
|
||||
"Upgrade to Plus or Pro": "升級到 Plus 或 Pro",
|
||||
"Current Plan": "當前方案",
|
||||
"Plan Limits": "方案限制",
|
||||
"Processing your payment...": "正在處理付款...",
|
||||
"Please wait while we confirm your subscription.": "請稍候,我們正在確認您的訂閱。",
|
||||
"Payment Processing": "付款處理中",
|
||||
"Your payment is being processed. This usually takes a few moments.": "您的付款正在處理中,通常需要幾秒鐘。",
|
||||
"Payment Failed": "付款失敗",
|
||||
"We couldn't process your subscription. Please try again or contact support if the issue persists.": "我們無法處理您的訂閱。請重試,或若問題持續,請聯絡客服。",
|
||||
"Back to Profile": "返回個人資料",
|
||||
"Subscription Successful!": "訂閱成功!",
|
||||
"Thank you for your subscription! Your payment has been processed successfully.": "感謝您的訂閱!您的付款已成功完成。",
|
||||
"Email:": "電子郵件:",
|
||||
"Plan:": "方案:",
|
||||
"Amount:": "金額:",
|
||||
"Subscription ID:": "訂閱 ID:",
|
||||
"Go to Library": "前往書庫",
|
||||
"Need help? Contact our support team at support@readest.com": "需要協助?請聯絡支援團隊:support@readest.com",
|
||||
"Free Plan": "免費方案",
|
||||
"month": "月",
|
||||
"AI Translations (per day)": "AI 翻譯(每日)",
|
||||
"Plus Plan": "Plus 方案",
|
||||
"Includes All Free Plan Benefits": "包含所有免費方案功能",
|
||||
"Pro Plan": "Pro 方案",
|
||||
"More AI Translations": "更多 AI 翻譯",
|
||||
"Complete Your Subscription": "完成您的訂閱",
|
||||
"{{percentage}}% of Cloud Sync Space Used.": "已使用 {{percentage}}% 的雲同步空間。",
|
||||
"Cloud Sync Storage": "雲同步空間",
|
||||
"Disable": "禁用",
|
||||
"Enable": "啟用",
|
||||
"Upgrade to Readest Premium": "升級到 Readest Premium",
|
||||
"Show Source Text": "顯示原文",
|
||||
"Cross-Platform Sync": "跨平台同步",
|
||||
"Seamlessly sync your library, progress, highlights, and notes across all your devices—never lose your place again.": "在所有裝置間無縫同步您的書庫、閱讀進度、標註和筆記——再也不會遺失閱讀位置。",
|
||||
"Customizable Reading": "個人化閱讀",
|
||||
"Personalize every detail with adjustable fonts, layouts, themes, and advanced display settings for the perfect reading experience.": "透過可調節的字體、版面配置、主題和進階顯示設定個人化每個細節,獲得完美閱讀體驗。",
|
||||
"AI Read Aloud": "AI 朗讀",
|
||||
"Enjoy hands-free reading with natural-sounding AI voices that bring your books to life.": "享受免手持的閱讀體驗,透過自然流暢的 AI 聲音讓書籍栩栩如生。",
|
||||
"AI Translations": "AI 翻譯",
|
||||
"Translate any text instantly with the power of Google, Azure, or DeepL—understand content in any language.": "借助 Google、Azure 或 DeepL 的強大功能即時翻譯任何文字——理解任何語言的內容。",
|
||||
"Connect with fellow readers and get help fast in our friendly community channels.": "與其他讀者交流,在我們友善的社群頻道中快速獲得幫助。",
|
||||
"Unlimited AI Read Aloud Hours": "無限 AI 朗讀時長",
|
||||
"Listen without limits—convert as much text as you like into immersive audio.": "無限制聆聽——將任意數量的文字轉換為沉浸式音訊。",
|
||||
"Unlock enhanced translation capabilities with more daily usage and advanced options.": "解鎖增強翻譯功能,享受更多翻譯用量和進階選項。",
|
||||
"DeepL Pro Access": "DeepL Pro 存取權限",
|
||||
"Enjoy faster responses and dedicated assistance whenever you need help.": "享受更快回應和專屬幫助,隨時獲得所需支援。",
|
||||
"Daily translation quota reached. Upgrade your plan to continue using AI translations.": "已達每日翻譯配額。升級方案以繼續使用 AI 翻譯。",
|
||||
"Includes All Plus Plan Benefits": "包含所有 Plus 方案優惠",
|
||||
"Early Feature Access": "搶先體驗新功能",
|
||||
"Be the first to explore new features, updates, and innovations before anyone else.": "搶先體驗新功能、更新和創新。",
|
||||
"Advanced AI Tools": "進階 AI 工具",
|
||||
"Harness powerful AI tools for smarter reading, translation, and content discovery.": "運用強大的 AI 工具實現智慧閱讀、翻譯和內容探索。",
|
||||
"Translate up to 100,000 characters daily with the most accurate translation engine available.": "使用最精準的翻譯引擎每天翻譯多達10萬字元。",
|
||||
"Translate up to 500,000 characters daily with the most accurate translation engine available.": "使用最精準的翻譯引擎每天翻譯多達50萬字元。",
|
||||
"Securely store and access your entire reading collection with up to 5 GB of cloud storage.": "使用多達 5GB 的安全雲端儲存保存和存取您的整個閱讀收藏。",
|
||||
"Securely store and access your entire reading collection with up to 20 GB of cloud storage.": "使用多達 20GB 的安全雲端儲存保存和存取您的整個閱讀收藏。",
|
||||
"Deleted cloud backup of the book: {{title}}": "已刪除書籍的雲端備份:{{title}}",
|
||||
"Are you sure to delete the cloud backup of the selected book?": "您確定要刪除所選書籍的雲端備份嗎?",
|
||||
"What's New in Readest": "Readest 更新功能",
|
||||
"Enter book title": "輸入書名",
|
||||
"Subtitle": "副標題",
|
||||
"Enter book subtitle": "輸入書籍副標題",
|
||||
"Enter author name": "輸入作者姓名",
|
||||
"Series": "系列",
|
||||
"Enter series name": "輸入系列名稱",
|
||||
"Series Index": "系列編號",
|
||||
"Enter series index": "輸入系列編號",
|
||||
"Total in Series": "系列總數",
|
||||
"Enter total books in series": "輸入系列中書籍總數",
|
||||
"Enter publisher": "輸入出版社",
|
||||
"Publication Date": "出版日期",
|
||||
"Identifier": "識別碼",
|
||||
"Enter book description": "輸入書籍描述",
|
||||
"Change cover image": "替換封面圖片",
|
||||
"Replace": "替換",
|
||||
"Unlock cover": "解鎖封面",
|
||||
"Lock cover": "鎖定封面",
|
||||
"Auto-Retrieve Metadata": "自動擷取中繼資料",
|
||||
"Auto-Retrieve": "自動擷取",
|
||||
"Unlock all fields": "解鎖所有欄位",
|
||||
"Unlock All": "全部解鎖",
|
||||
"Lock all fields": "鎖定所有欄位",
|
||||
"Lock All": "全部鎖定",
|
||||
"Reset": "重設",
|
||||
"Edit Metadata": "編輯中繼資料",
|
||||
"Locked": "已鎖定",
|
||||
"Select Metadata Source": "選擇中繼資料來源",
|
||||
"Keep manual input": "保留手動輸入",
|
||||
"Google Books": "Google 圖書",
|
||||
"Open Library": "開放圖書館",
|
||||
"Fiction, Science, History": "小說,科學,歷史",
|
||||
"Select Cover Image": "選擇封面圖片",
|
||||
"Open Book in New Window": "在新視窗中打開書籍",
|
||||
"Voices for {{lang}}": "{{lang}} 的語音",
|
||||
"Yandex Translate": "Yandex 翻譯",
|
||||
"YYYY or YYYY-MM-DD": "YYYY 或 YYYY-MM-DD",
|
||||
"Restore Purchase": "恢復購買",
|
||||
"No purchases found to restore.": "未找到可恢復的購買。",
|
||||
"Failed to restore purchases.": "恢復購買失敗。",
|
||||
"Failed to manage subscription.": "管理訂閱失敗。",
|
||||
"Failed to load subscription plans.": "加載訂閱計劃失敗。",
|
||||
"year": "年",
|
||||
"Failed to create checkout session": "創建結帳會話失敗",
|
||||
"Storage": "雲同步空間",
|
||||
"Terms of Service": "服務條款",
|
||||
"Privacy Policy": "隱私政策",
|
||||
"Disable Double Click": "禁用雙擊",
|
||||
"TTS not supported for this document": "TTS 不支援此文件",
|
||||
"Reset Password": "重設密碼",
|
||||
"Show Reading Progress": "顯示閱讀進度",
|
||||
"Reading Progress Style": "閱讀進度樣式",
|
||||
"Page Number": "頁碼",
|
||||
"Percentage": "百分比",
|
||||
"Deleted local copy of the book: {{title}}": "已刪除書籍的本地副本:{{title}}",
|
||||
"Failed to delete cloud backup of the book: {{title}}": "未能刪除書籍的雲端備份:{{title}}",
|
||||
"Failed to delete local copy of the book: {{title}}": "未能刪除書籍的本地副本:{{title}}",
|
||||
"Are you sure to delete the local copy of the selected book?": "您確定要刪除所選書籍的本地副本嗎?",
|
||||
"Remove from Cloud & Device": "從雲端和設備中移除",
|
||||
"Remove from Cloud Only": "僅從雲端移除",
|
||||
"Remove from Device Only": "僅從設備中移除",
|
||||
"Disconnected": "已斷開連接",
|
||||
"KOReader Sync Settings": "KOReader 同步設置",
|
||||
"Sync Strategy": "同步策略",
|
||||
"Ask on conflict": "發生衝突時詢問",
|
||||
"Always use latest": "始終使用最新",
|
||||
"Send changes only": "僅發送更改",
|
||||
"Receive changes only": "僅接收更改",
|
||||
"Disabled": "已禁用",
|
||||
"Checksum Method": "校驗和方法",
|
||||
"File Content (recommended)": "文件內容(推薦)",
|
||||
"File Name": "文件名",
|
||||
"Device Name": "設備名稱",
|
||||
"Sync Tolerance": "同步容忍度",
|
||||
"Connect to your KOReader Sync server.": "連接到您的 KOReader 同步伺服器。",
|
||||
"Server URL": "伺服器 URL",
|
||||
"Username": "用戶名",
|
||||
"Your Username": "您的用戶名",
|
||||
"Password": "密碼",
|
||||
"Connect": "連接",
|
||||
"KOReader Sync": "KOReader 同步",
|
||||
"Sync Conflict": "同步衝突",
|
||||
"Sync reading progress from \"{{deviceName}}\"?": "從 \"{{deviceName}}\" 同步閱讀進度?",
|
||||
"another device": "另一個設備",
|
||||
"Local Progress": "本地進度",
|
||||
"Remote Progress": "遠程進度",
|
||||
"Page {{page}} of {{total}} ({{percentage}}%)": "第 {{page}} 頁,共 {{total}} 頁 ({{percentage}}%)",
|
||||
"Current position": "當前位置",
|
||||
"Approximately page {{page}} of {{total}} ({{percentage}}%)": "大約第 {{page}} 頁,共 {{total}} 頁 ({{percentage}}%)",
|
||||
"Approximately {{percentage}}%": "大約 {{percentage}}%",
|
||||
"Failed to connect": "連接失敗",
|
||||
"Sync Server Connected": "同步伺服器已連接",
|
||||
"Precision: {{precision}} digits after the decimal": "精度: {{precision}} 位小數",
|
||||
"Sync as {{userDisplayName}}": "以 {{userDisplayName}} 身份同步"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,179 @@
|
||||
{
|
||||
"releases": {
|
||||
"0.9.72": {
|
||||
"date": "2025-08-18",
|
||||
"notes": [
|
||||
"Fixed Edge TTS voice playback for EPUBs",
|
||||
"Reduced accidental page flips when toggling toolbars",
|
||||
"Fixed images with background colors not displaying correctly in some books",
|
||||
"Added support for custom KOReader Sync Servers on your local network (LAN)"
|
||||
]
|
||||
},
|
||||
"0.9.71": {
|
||||
"date": "2025-08-13",
|
||||
"notes": [
|
||||
"Sync: Added two ways to sync reading progress with KOReader devices",
|
||||
"EPUB: Applied monospace font settings",
|
||||
"EPUB: Fixed initial text alignment for some EPUB files",
|
||||
"PDF: Fixed issue opening large PDF files on Android",
|
||||
"TTS: Improved book language detection when language code is invalid",
|
||||
"iOS: Fixed applying system color scheme in auto theme mode",
|
||||
"Config: Added option to choose reading progress display style (percentage or page number)",
|
||||
"Library: Added option to delete only the local copy of a book",
|
||||
"Account: Added 'Reset Password' button in the account page"
|
||||
]
|
||||
},
|
||||
"0.9.69": {
|
||||
"date": "2025-08-02",
|
||||
"notes": [
|
||||
"Updater: Fixed permission issue when updating in standalone reader window",
|
||||
"Config: Added option to disable double‑click actions",
|
||||
"EPUB: Fixed missing images in some EPUB files",
|
||||
"EPUB: Corrected formatting for book subjects",
|
||||
"TXT: Improved language detection when parsing TXT files",
|
||||
"CSS: Resolved issue where custom font colors could not be applied",
|
||||
"Layout: Corrected cover image width display in 'Fit' mode for list view",
|
||||
"Layout: Fixed unintended scrolling in reader view when highlighting text"
|
||||
]
|
||||
},
|
||||
"0.9.68": {
|
||||
"date": "2025-07-30",
|
||||
"notes": [
|
||||
"Reader: Files now open in a new window by default on Desktop",
|
||||
"Translation: Skips translating content inside <pre>, <code>, and <math> tags",
|
||||
"Performance: Improved multi-part downloading for large books",
|
||||
"Sync: Resolved issue preventing some PDF files from syncing",
|
||||
"CSS: Resolved issue with fixed font color in some EPUB files",
|
||||
"TXT: Enhanced TXT parsing for better compatibility"
|
||||
]
|
||||
},
|
||||
"0.9.67": {
|
||||
"date": "2025-07-22",
|
||||
"notes": [
|
||||
"Reader: Fixed file opening issue when filenames contain commas",
|
||||
"Translation: Added Yandex Translator as a new translation provider",
|
||||
"Layout: Hovering over header now reveals macOS traffic light window controls",
|
||||
"Theme: Reader background color is now customizable for EPUB and PDF",
|
||||
"Library: Search now includes book format, group names, and descriptions",
|
||||
"Library: Various improvements to metadata editor and bookshelf management",
|
||||
"Linux: Fixed social login failure on Linux systems"
|
||||
]
|
||||
},
|
||||
"0.9.66": {
|
||||
"date": "2025-07-21",
|
||||
"notes": [
|
||||
"Layout: Fixed nested table of contents items not expanding in long TOC lists",
|
||||
"Reader: Opening files from the OS now takes highest priority",
|
||||
"TTS: Improved voice filtering for some language codes",
|
||||
"App: Fixed loading issues on older browsers",
|
||||
"App: Fixed wrong MIME types generated on Linux"
|
||||
]
|
||||
},
|
||||
"0.9.65": {
|
||||
"date": "2025-07-19",
|
||||
"notes": [
|
||||
"Reader: Added support for opening multiple reader windows on desktop",
|
||||
"Layout: Added visible window borders on Linux",
|
||||
"TTS: Improved handling of invalid book language",
|
||||
"TTS: Skipped reading footnote anchor links",
|
||||
"Translation: Fixed an issue where table of contents entries were not translated",
|
||||
"Sync: Edited book metadata is now included in sync across devices",
|
||||
"iOS: Disabled context menu when long-pressing on book covers"
|
||||
]
|
||||
},
|
||||
"0.9.64": {
|
||||
"date": "2025-07-16",
|
||||
"notes": [
|
||||
"Library: Added support to edit book metadata",
|
||||
"Layout: Fixed bookmark icon placement when the sidebar is pinned",
|
||||
"Reader: Fixed an issue where links to other chapters in EPUB files did not work",
|
||||
"Reader: Lightened the highlight color in dark mode for better contrast",
|
||||
"Reader: Pull-to-refresh gesture is now smoother and more responsive",
|
||||
"Reader: Removed unintended indentation on images",
|
||||
"Search: Improved responsiveness of full-text search for faster results"
|
||||
]
|
||||
},
|
||||
"0.9.63": {
|
||||
"date": "2025-07-07",
|
||||
"notes": [
|
||||
"Reader: Fixed links not working in some MOBI books",
|
||||
"TTS: Fixed an issue where some text was skipped during reading",
|
||||
"Library: You can now delete only the cloud backup of a book without removing the local copy",
|
||||
"Performance: Fonts in the font list now load only when displayed for faster startup",
|
||||
"Performance: Opening large MOBI books is now significantly faster",
|
||||
"Layout: Fixed overlay scrollbar display in longer TOC items list",
|
||||
"Layout: Improved grid cover image height in crop mode",
|
||||
"Layout: Removed unsupported screen orientation lock option on iPad",
|
||||
"Layout: System context menu no longer appears when selecting text on iPad",
|
||||
"Translation: Added Thai (th-TH) language support"
|
||||
]
|
||||
},
|
||||
"0.9.62": {
|
||||
"date": "2025-07-02",
|
||||
"notes": [
|
||||
"Subscription: You can now upgrade to Readest Premium and manage your subscription",
|
||||
"Reader: Added an option to toggle Parallel Reading when viewing multiple books",
|
||||
"Translation: Added an option to show or hide the original text in translations",
|
||||
"Layout: TOC on Android now uses an overlay scrollbar for easier navigation",
|
||||
"Layout: Custom CSS can now also be applied to the library page",
|
||||
"Fonts: Added three new CJK fonts to choose from",
|
||||
"Shortcuts: Press ESC to quickly close the search bar"
|
||||
]
|
||||
},
|
||||
"0.9.61": {
|
||||
"date": "2025-06-26",
|
||||
"notes": [
|
||||
"Layout: Fixed view menu width on smaller screens",
|
||||
"Layout: Added option to crop or fit book covers in library view",
|
||||
"Reader: You can now apply custom CSS to the reading interface (UI)",
|
||||
"Reader: Added an option to show remaining pages in the current chapter",
|
||||
"Settings: Added options to reset settings from the config dialog",
|
||||
"Import: Fixed encoded filenames when importing TXT files on iOS",
|
||||
"Linux: Fixed missing app icon on some Linux distributions",
|
||||
"GitHub: Added donation link to support Readest development"
|
||||
]
|
||||
},
|
||||
"0.9.60": {
|
||||
"date": "2025-06-22",
|
||||
"notes": [
|
||||
"Layout has been improved for iPad devices",
|
||||
"Screen orientation changes are now smoother on iOS",
|
||||
"Importing books on iOS during the first launch is now more reliable",
|
||||
"Font size no longer changes unexpectedly in scrolled mode on iOS",
|
||||
"Top and bottom safe insets are now ignored when no header or footer is present",
|
||||
"Interactive books now load correctly on the Desktop platform",
|
||||
"Translation is now disabled when the primary language is undefined",
|
||||
"Android: the task list background color has been updated with current theme color"
|
||||
]
|
||||
},
|
||||
"0.9.59": {
|
||||
"date": "2025-06-19",
|
||||
"notes": [
|
||||
"Layout: Image size now adjusts more accurately by converting screen units to pixels properly",
|
||||
"Layout: Improved header layout on iOS landscape mode — extra spacing has been removed",
|
||||
"Settings: Top and bottom margin settings are now more intuitive and better constrained"
|
||||
]
|
||||
},
|
||||
"0.9.58": {
|
||||
"date": "2025-06-18",
|
||||
"notes": [
|
||||
"TTS: Translation now works even when TTS is playing in the background",
|
||||
"TTS: Press T on your keyboard to quickly toggle TTS on or off",
|
||||
"TTS: Each open book now has its own independent TTS controller",
|
||||
"Annotations: Annotation tools now function properly while TTS is active",
|
||||
"Settings: Margins for top, bottom, left, and right can now be adjusted individually",
|
||||
"Settings: New option to always show the status bar while reading",
|
||||
"Layout: Reader page now adapts better to device safe areas for a more comfortable layout",
|
||||
"UI: Added creation timestamps to annotations and made various interface improvements",
|
||||
"iPad: Multiple columns are now supported in portrait mode",
|
||||
"iPad: Fixed issue where sidebars couldn't be resized by dragging",
|
||||
"iPad: Added support for split-screen mode"
|
||||
]
|
||||
},
|
||||
"0.9.57": {
|
||||
"date": "2025-06-14",
|
||||
"notes": ["TTS: Fixed an issue to detect custom TTS engines on some Android devices"]
|
||||
},
|
||||
"0.9.56": {
|
||||
"date": "2025-06-13",
|
||||
"notes": [
|
||||
@@ -201,7 +375,7 @@
|
||||
"0.9.30": {
|
||||
"date": "2025-04-05",
|
||||
"notes": [
|
||||
"Added fixed storage quata when running the app in self-hosted mode",
|
||||
"Added fixed storage quota when running the app in self-hosted mode",
|
||||
"Link colors will only change when you override fonts",
|
||||
"Added cache for native files for better performance"
|
||||
]
|
||||
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
VERSION=$(jq -r '.version' package.json)
|
||||
|
||||
if [[ -z "$VERSION" ]]; then
|
||||
echo "❌ Failed to extract version from package.json"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "📦 Found version: $VERSION"
|
||||
|
||||
IFS='.' read -r MAJOR MINOR PATCH <<< "$VERSION"
|
||||
|
||||
if [[ -z "$MAJOR" || -z "$MINOR" || -z "$PATCH" ]]; then
|
||||
echo "❌ Invalid version format: $VERSION"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Convert x.y.z => x * 10000 + y * 1000 + z
|
||||
VERSION_CODE=$((10#$MAJOR * 10000 + 10#$MINOR * 1000 + 10#$PATCH))
|
||||
echo "🔢 Computed versionCode: $VERSION_CODE"
|
||||
|
||||
PROPERTIES_FILE="./src-tauri/gen/android/app/tauri.properties"
|
||||
|
||||
if [[ ! -f "$PROPERTIES_FILE" ]]; then
|
||||
echo "❌ File not found: $PROPERTIES_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
tmpfile=$(mktemp)
|
||||
sed "s/^tauri\.android\.versionName=.*/tauri.android.versionName=$VERSION/" "$PROPERTIES_FILE" | \
|
||||
sed "s/^tauri\.android\.versionCode=.*/tauri.android.versionCode=$VERSION_CODE/" > "$tmpfile"
|
||||
mv "$tmpfile" "$PROPERTIES_FILE"
|
||||
|
||||
echo "✅ Updated $PROPERTIES_FILE"
|
||||
|
||||
echo "🚀 Running: pnpm tauri android build"
|
||||
pnpm tauri android build
|
||||
|
||||
source .env.google-play.local
|
||||
if [[ -z "$GOOGLE_PLAY_JSON_KEY_FILE" ]]; then
|
||||
echo "❌ GOOGLE_PLAY_JSON_KEY_FILE is not set"
|
||||
exit 1
|
||||
fi
|
||||
cd ../../
|
||||
fastlane android upload_production
|
||||
@@ -14,8 +14,12 @@ rust-version = "1.77.2"
|
||||
name = "readestlib"
|
||||
crate-type = ["staticlib", "cdylib", "lib"]
|
||||
|
||||
[features]
|
||||
# Internal feature to suppress warnings from old objc crate
|
||||
cargo-clippy = []
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { version = "2.1.1", features = [] }
|
||||
tauri-build = { version = "2", features = [] }
|
||||
|
||||
[dependencies]
|
||||
serde_json = "1.0"
|
||||
@@ -25,26 +29,26 @@ thiserror = "2"
|
||||
tokio = { version = "1", features = ["fs"] }
|
||||
tokio-util = { version = "0.7", features = ["codec"] }
|
||||
futures-util = "0.3"
|
||||
futures = "0.3.31"
|
||||
read-progress-stream = "1.0.0"
|
||||
reqwest = { version = "0.12", default-features = false, features = [
|
||||
"json",
|
||||
"stream",
|
||||
] }
|
||||
# FIXME: remove the devtools feature in production
|
||||
tauri = { version = "2.5.1", features = [ "protocol-asset", "devtools"] }
|
||||
tauri-build = "2.1.1"
|
||||
tauri-plugin-log = "2.4.0"
|
||||
tauri-plugin-fs = "2.2.1"
|
||||
tauri-plugin-dialog = "2.2.1"
|
||||
tauri-plugin-os = "2.2.1"
|
||||
tauri-plugin-http = "2.4.3"
|
||||
tauri-plugin-shell = "2.2.1"
|
||||
tauri-plugin-process = "2.2.1"
|
||||
tauri = { version = "2.5.1", features = [ "protocol-asset" ] }
|
||||
tauri-build = "2"
|
||||
tauri-plugin-log = "2"
|
||||
tauri-plugin-fs = "2"
|
||||
tauri-plugin-dialog = "2"
|
||||
tauri-plugin-os = "2"
|
||||
tauri-plugin-http = { version = "2", features = ["dangerous-settings"] }
|
||||
tauri-plugin-shell = "2"
|
||||
tauri-plugin-process = "2"
|
||||
tauri-plugin-oauth = "2"
|
||||
tauri-plugin-opener = "2.2.6"
|
||||
tauri-plugin-deep-link = "2.2.1"
|
||||
tauri-plugin-opener = "2"
|
||||
tauri-plugin-deep-link = "2"
|
||||
tauri-plugin-sign-in-with-apple = "1.0.2"
|
||||
tauri-plugin-haptics = "2.2.4"
|
||||
tauri-plugin-haptics = "2"
|
||||
tauri-plugin-native-bridge = { path = "./plugins/tauri-plugin-native-bridge" }
|
||||
tauri-plugin-native-tts = { path = "./plugins/tauri-plugin-native-tts" }
|
||||
|
||||
@@ -61,6 +65,6 @@ objc2-foundation = { version = "0.3", features = ["NSError", "NSArray"] }
|
||||
|
||||
[target.'cfg(any(target_os = "macos", windows, target_os = "linux"))'.dependencies]
|
||||
tauri-plugin-cli = "2"
|
||||
tauri-plugin-single-instance = "2.2.3"
|
||||
tauri-plugin-updater = "2.7.0"
|
||||
tauri-plugin-window-state = "2.2.2"
|
||||
tauri-plugin-single-instance = "2"
|
||||
tauri-plugin-updater = "2"
|
||||
tauri-plugin-window-state = "2"
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<key>UIHomeIndicatorAutoHidden</key>
|
||||
<true/>
|
||||
<key>UIRequiresFullScreen</key>
|
||||
<true/>
|
||||
<false/>
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
@@ -198,4 +198,4 @@
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
</plist>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"$schema": "../gen/schemas/desktop-schema.json",
|
||||
"identifier": "default",
|
||||
"description": "enables the default permissions",
|
||||
"windows": ["main", "updater"],
|
||||
"windows": ["main", "updater", "reader-*"],
|
||||
"permissions": [
|
||||
"core:default",
|
||||
"fs:default",
|
||||
@@ -43,7 +43,18 @@
|
||||
"identifier": "fs:allow-cache-read",
|
||||
"allow": [
|
||||
{
|
||||
"path": "$CACHEDIR/**/*"
|
||||
"path": "$APPCACHE/**/*"
|
||||
},
|
||||
{
|
||||
"path": "/private/var/mobile/Containers/Data/Application/**/*"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"identifier": "fs:allow-cache-write",
|
||||
"allow": [
|
||||
{
|
||||
"path": "$APPCACHE/**/*"
|
||||
},
|
||||
{
|
||||
"path": "/private/var/mobile/Containers/Data/Application/**/*"
|
||||
@@ -54,22 +65,34 @@
|
||||
"identifier": "http:default",
|
||||
"allow": [
|
||||
{
|
||||
"url": "https://*.deepl.com"
|
||||
"url": "https://*.readest.com"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/readest/*"
|
||||
},
|
||||
{
|
||||
"url": "https://*.deepl.com"
|
||||
},
|
||||
{
|
||||
"url": "https://*.cloudflarestorage.com"
|
||||
},
|
||||
{
|
||||
"url": "https://edge.microsoft.com"
|
||||
},
|
||||
{
|
||||
"url": "https://translate.toil.cc"
|
||||
},
|
||||
{
|
||||
"url": "https://*.microsofttranslator.com"
|
||||
},
|
||||
{
|
||||
"url": "https://translate.googleapis.com"
|
||||
},
|
||||
{
|
||||
"url": "http://*:*"
|
||||
},
|
||||
{
|
||||
"url": "https://*:*"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -88,6 +111,7 @@
|
||||
"core:window:allow-toggle-maximize",
|
||||
"core:window:allow-set-fullscreen",
|
||||
"core:window:allow-set-always-on-top",
|
||||
"core:window:allow-destroy",
|
||||
"core:webview:allow-create-webview-window",
|
||||
"core:path:allow-resolve-directory",
|
||||
"log:default",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "../gen/schemas/desktop-schema.json",
|
||||
"identifier": "desktop-capability",
|
||||
"windows": ["main", "updater"],
|
||||
"windows": ["main", "updater", "reader-*"],
|
||||
"platforms": ["linux", "macOS", "windows"],
|
||||
"permissions": ["updater:default", "cli:default"]
|
||||
}
|
||||
|
||||
@@ -15,28 +15,29 @@ val tauriProperties = Properties().apply {
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdk = 34
|
||||
compileSdk = 36
|
||||
namespace = "com.bilingify.readest"
|
||||
val keystorePropertiesFile = rootProject.file("keystore.properties")
|
||||
val keystoreProperties = Properties()
|
||||
if (keystorePropertiesFile.exists()) {
|
||||
keystoreProperties.load(FileInputStream(keystorePropertiesFile))
|
||||
}
|
||||
defaultConfig {
|
||||
manifestPlaceholders["usesCleartextTraffic"] = "false"
|
||||
applicationId = "com.bilingify.readest"
|
||||
minSdk = 26
|
||||
targetSdk = 34
|
||||
targetSdk = 36
|
||||
versionCode = tauriProperties.getProperty("tauri.android.versionCode", "1").toInt()
|
||||
versionName = tauriProperties.getProperty("tauri.android.versionName", "1.0")
|
||||
}
|
||||
signingConfigs {
|
||||
create("release") {
|
||||
val keystorePropertiesFile = rootProject.file("keystore.properties")
|
||||
val keystoreProperties = Properties()
|
||||
if (keystorePropertiesFile.exists()) {
|
||||
keystoreProperties.load(FileInputStream(keystorePropertiesFile))
|
||||
if (keystorePropertiesFile.exists()) {
|
||||
create("signing") {
|
||||
keyAlias = keystoreProperties["keyAlias"] as String
|
||||
keyPassword = keystoreProperties["password"] as String
|
||||
storeFile = file(keystoreProperties["storeFile"] as String)
|
||||
storePassword = keystoreProperties["password"] as String
|
||||
}
|
||||
|
||||
keyAlias = keystoreProperties["keyAlias"] as String
|
||||
keyPassword = keystoreProperties["password"] as String
|
||||
storeFile = file(keystoreProperties["storeFile"] as String)
|
||||
storePassword = keystoreProperties["password"] as String
|
||||
}
|
||||
}
|
||||
buildTypes {
|
||||
@@ -45,14 +46,20 @@ android {
|
||||
isDebuggable = true
|
||||
isJniDebuggable = true
|
||||
isMinifyEnabled = false
|
||||
packaging { jniLibs.keepDebugSymbols.add("*/arm64-v8a/*.so")
|
||||
if (keystorePropertiesFile.exists()) {
|
||||
signingConfig = signingConfigs.getByName("signing")
|
||||
}
|
||||
packaging {
|
||||
jniLibs.keepDebugSymbols.add("*/arm64-v8a/*.so")
|
||||
jniLibs.keepDebugSymbols.add("*/armeabi-v7a/*.so")
|
||||
jniLibs.keepDebugSymbols.add("*/x86/*.so")
|
||||
jniLibs.keepDebugSymbols.add("*/x86_64/*.so")
|
||||
}
|
||||
}
|
||||
getByName("release") {
|
||||
signingConfig = signingConfigs.getByName("release")
|
||||
if (keystorePropertiesFile.exists()) {
|
||||
signingConfig = signingConfigs.getByName("signing")
|
||||
}
|
||||
}
|
||||
}
|
||||
kotlinOptions {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
|
||||
@@ -10,10 +9,11 @@
|
||||
|
||||
<application
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher"
|
||||
android:largeHeap="true"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/Theme.readest"
|
||||
android:hardwareAccelerated="true"
|
||||
android:hardwareAccelerated="true"
|
||||
android:usesCleartextTraffic="${usesCleartextTraffic}">
|
||||
<activity
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode"
|
||||
@@ -42,15 +42,15 @@
|
||||
<data android:mimeType="application/zip" />
|
||||
<data android:mimeType="text/plain" />
|
||||
|
||||
<data android:pathPattern=".*\\.epub" />
|
||||
<data android:pathPattern=".*\\.pdf" />
|
||||
<data android:pathPattern=".*\\.fb2" />
|
||||
<data android:pathPattern=".*\\.fb2.zip" />
|
||||
<data android:pathPattern=".*\\.mobi" />
|
||||
<data android:pathPattern=".*\\.azw" />
|
||||
<data android:pathPattern=".*\\.cbz" />
|
||||
<data android:pathPattern=".*\\.zip" />
|
||||
<data android:pathPattern=".*\\.txt" />
|
||||
<data android:pathPattern="/.*\\.epub" />
|
||||
<data android:pathPattern="/.*\\.pdf" />
|
||||
<data android:pathPattern="/.*\\.fb2" />
|
||||
<data android:pathPattern="/.*\\.fb2.zip" />
|
||||
<data android:pathPattern="/.*\\.mobi" />
|
||||
<data android:pathPattern="/.*\\.azw" />
|
||||
<data android:pathPattern="/.*\\.cbz" />
|
||||
<data android:pathPattern="/.*\\.zip" />
|
||||
<data android:pathPattern="/.*\\.txt" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter android:label="oauth">
|
||||
@@ -70,10 +70,13 @@
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="http" />
|
||||
<data android:scheme="https" />
|
||||
<data android:scheme="http" />
|
||||
<data android:host="web.readest.com" />
|
||||
|
||||
|
||||
|
||||
|
||||
</intent-filter>
|
||||
<!-- DEEP LINK PLUGIN. AUTO-GENERATED. DO NOT REMOVE. -->
|
||||
</activity>
|
||||
|
||||
+14
@@ -1,10 +1,14 @@
|
||||
package com.bilingify.readest
|
||||
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import android.view.KeyEvent
|
||||
import android.webkit.WebView
|
||||
import android.util.Log
|
||||
import android.graphics.Color
|
||||
import android.app.ActivityManager
|
||||
import android.content.res.Configuration
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
@@ -77,5 +81,15 @@ class MainActivity : TauriActivity(), KeyDownInterceptor {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
enableEdgeToEdge()
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
setTaskDescription(
|
||||
ActivityManager.TaskDescription(
|
||||
getString(R.string.app_name),
|
||||
null,
|
||||
Color.TRANSPARENT
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
<!-- Base application theme. -->
|
||||
<style name="Theme.readest" parent="Theme.MaterialComponents.DayNight.NoActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="android:windowSplashScreenBackground">@android:color/transparent</item>
|
||||
<item name="android:windowSplashScreenAnimatedIcon" tools:targetApi="31">@null</item>
|
||||
|
||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||
<item name="android:navigationBarColor">@android:color/transparent</item>
|
||||
<item name="android:windowTranslucentStatus">false</item>
|
||||
|
||||
@@ -9,13 +9,13 @@ exclude = ["/examples", "/dist-js", "/guest-js", "/node_modules"]
|
||||
links = "tauri-plugin-native-bridge"
|
||||
|
||||
[dependencies]
|
||||
tauri = { version = "2.3.1" }
|
||||
tauri = { version = "2" }
|
||||
serde = "1.0"
|
||||
thiserror = "2"
|
||||
schemars = "0.8"
|
||||
|
||||
[build-dependencies]
|
||||
tauri-plugin = { version = "2.0.4", features = ["build"] }
|
||||
tauri-plugin = { version = "2", features = ["build"] }
|
||||
schemars = "0.8"
|
||||
|
||||
[target.'cfg(any(target_os = "macos", windows, target_os = "linux"))'.dependencies]
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ plugins {
|
||||
|
||||
android {
|
||||
namespace = "com.readest.native_bridge"
|
||||
compileSdk = 34
|
||||
compileSdk = 36
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 21
|
||||
|
||||
@@ -9,6 +9,11 @@ const COMMANDS: &[&str] = &[
|
||||
"get_sys_fonts_list",
|
||||
"intercept_keys",
|
||||
"lock_screen_orientation",
|
||||
"iap_initialize",
|
||||
"iap_fetch_products",
|
||||
"iap_purchase_product",
|
||||
"iap_restore_purchases",
|
||||
"get_system_color_scheme",
|
||||
];
|
||||
|
||||
fn main() {
|
||||
|
||||
+220
-36
@@ -3,6 +3,7 @@ import AuthenticationServices
|
||||
import CoreText
|
||||
import MediaPlayer
|
||||
import ObjectiveC
|
||||
import StoreKit
|
||||
import SwiftRs
|
||||
import Tauri
|
||||
import UIKit
|
||||
@@ -48,6 +49,37 @@ class LockScreenOrientationRequestArgs: Decodable {
|
||||
let orientation: String?
|
||||
}
|
||||
|
||||
struct InitializeRequest: Decodable {
|
||||
let publicKey: String?
|
||||
}
|
||||
|
||||
struct FetchProductsRequest: Decodable {
|
||||
let productIds: [String]
|
||||
}
|
||||
|
||||
struct PurchaseProductRequest: Decodable {
|
||||
let productId: String
|
||||
}
|
||||
|
||||
struct ProductData: Codable {
|
||||
let id: String
|
||||
let title: String
|
||||
let description: String
|
||||
let price: String
|
||||
let priceCurrencyCode: String?
|
||||
let priceAmountMicros: Int64
|
||||
let productType: String
|
||||
}
|
||||
|
||||
struct PurchaseData: Codable {
|
||||
let productId: String
|
||||
let transactionId: String
|
||||
let originalTransactionId: String
|
||||
let purchaseDate: String
|
||||
let purchaseState: String
|
||||
let platform: String
|
||||
}
|
||||
|
||||
class VolumeKeyHandler: NSObject {
|
||||
private var audioSession: AVAudioSession?
|
||||
private var originalVolume: Float = 0.0
|
||||
@@ -154,9 +186,8 @@ class VolumeKeyHandler: NSObject {
|
||||
class NativeBridgePlugin: Plugin {
|
||||
private var webView: WKWebView?
|
||||
private var authSession: ASWebAuthenticationSession?
|
||||
private var isOrientationLocked = false
|
||||
private var currentOrientationMask: UIInterfaceOrientationMask = .all
|
||||
private var orientationObserver: NSObjectProtocol?
|
||||
private var originalDelegate: UIApplicationDelegate?
|
||||
|
||||
@objc public override func load(webview: WKWebView) {
|
||||
self.webView = webview
|
||||
@@ -175,16 +206,19 @@ class NativeBridgePlugin: Plugin {
|
||||
name: UIApplication.didEnterBackgroundNotification,
|
||||
object: nil
|
||||
)
|
||||
|
||||
if let app = UIApplication.value(forKey: "sharedApplication") as? UIApplication {
|
||||
self.originalDelegate = app.delegate
|
||||
app.delegate = self
|
||||
} else {
|
||||
Logger.error("NativeBridgePlugin: Failed to get shared application")
|
||||
}
|
||||
}
|
||||
|
||||
@objc func appDidBecomeActive() {
|
||||
if volumeKeyHandler != nil {
|
||||
activateVolumeKeyInterception()
|
||||
}
|
||||
|
||||
if orientationObserver != nil {
|
||||
self.setupOrientationObserver()
|
||||
}
|
||||
}
|
||||
|
||||
@objc func appDidEnterBackground() {
|
||||
@@ -360,18 +394,11 @@ class NativeBridgePlugin: Plugin {
|
||||
let orientation = args.orientation ?? "auto"
|
||||
switch orientation.lowercased() {
|
||||
case "portrait":
|
||||
self.isOrientationLocked = true
|
||||
self.currentOrientationMask = .portrait
|
||||
self.forceOrientation(.portrait)
|
||||
self.setupOrientationObserver()
|
||||
self.changeOrientation(.portrait)
|
||||
case "landscape":
|
||||
self.isOrientationLocked = true
|
||||
self.currentOrientationMask = .landscape
|
||||
self.forceOrientation(.landscapeRight)
|
||||
self.setupOrientationObserver()
|
||||
self.changeOrientation(.landscape)
|
||||
case "auto":
|
||||
self.isOrientationLocked = false
|
||||
self.currentOrientationMask = .all
|
||||
self.changeOrientation(.all)
|
||||
default:
|
||||
invoke.reject("Invalid orientation mode")
|
||||
return
|
||||
@@ -381,43 +408,137 @@ class NativeBridgePlugin: Plugin {
|
||||
}
|
||||
}
|
||||
|
||||
private func forceOrientation(_ orientation: UIInterfaceOrientation) {
|
||||
private func changeOrientation(_ orientationMask: UIInterfaceOrientationMask) {
|
||||
self.currentOrientationMask = orientationMask
|
||||
if #available(iOS 16.0, *) {
|
||||
if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene {
|
||||
let orientationMask: UIInterfaceOrientationMask =
|
||||
orientation.isPortrait ? .portrait : .landscape
|
||||
|
||||
for window in windowScene.windows {
|
||||
if let rootVC = window.rootViewController {
|
||||
rootVC.setNeedsUpdateOfSupportedInterfaceOrientations()
|
||||
}
|
||||
}
|
||||
|
||||
windowScene.requestGeometryUpdate(.iOS(interfaceOrientations: orientationMask)) { error in
|
||||
print("Orientation update error: \(error.localizedDescription)")
|
||||
if orientationMask == .all {
|
||||
windowScene.requestGeometryUpdate(.iOS(interfaceOrientations: .all)) { error in
|
||||
logger.error("Orientation update error: \(error.localizedDescription)")
|
||||
DispatchQueue.main.async {
|
||||
UIViewController.attemptRotationToDeviceOrientation()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
windowScene.requestGeometryUpdate(.iOS(interfaceOrientations: orientationMask)) { error in
|
||||
logger.error("Orientation update error: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
UIDevice.current.setValue(orientation.rawValue, forKey: "orientation")
|
||||
UIViewController.attemptRotationToDeviceOrientation()
|
||||
if orientationMask == .all {
|
||||
UIViewController.attemptRotationToDeviceOrientation()
|
||||
} else {
|
||||
let specificOrientation: UIInterfaceOrientation
|
||||
if orientationMask.contains(.portrait) {
|
||||
specificOrientation = .portrait
|
||||
} else if orientationMask.contains(.landscape) {
|
||||
let currentOrientation = UIDevice.current.orientation
|
||||
if currentOrientation == .landscapeLeft {
|
||||
specificOrientation = .landscapeRight
|
||||
} else if currentOrientation == .landscapeRight {
|
||||
specificOrientation = .landscapeLeft
|
||||
} else {
|
||||
specificOrientation = .landscapeRight
|
||||
}
|
||||
} else {
|
||||
specificOrientation = .portrait
|
||||
}
|
||||
UIDevice.current.setValue(specificOrientation.rawValue, forKey: "orientation")
|
||||
UIViewController.attemptRotationToDeviceOrientation()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func setupOrientationObserver() {
|
||||
orientationObserver = NotificationCenter.default.addObserver(
|
||||
forName: UIDevice.orientationDidChangeNotification,
|
||||
object: nil,
|
||||
queue: .main
|
||||
) { [weak self] _ in
|
||||
guard let self = self, self.isOrientationLocked else { return }
|
||||
@objc public func iap_initialize(_ invoke: Invoke) {
|
||||
StoreKitManager.shared.initialize()
|
||||
invoke.resolve(["success": true])
|
||||
}
|
||||
|
||||
if self.currentOrientationMask == .portrait {
|
||||
self.forceOrientation(.portrait)
|
||||
} else if self.currentOrientationMask == .landscape {
|
||||
self.forceOrientation(.landscapeRight)
|
||||
@objc public func iap_fetch_products(_ invoke: Invoke) {
|
||||
do {
|
||||
let args = try invoke.parseArgs(FetchProductsRequest.self)
|
||||
|
||||
StoreKitManager.shared.fetchProducts(productIds: args.productIds) { products in
|
||||
let productsData: [ProductData] = products.map { product in
|
||||
return ProductData(
|
||||
id: product.productIdentifier,
|
||||
title: product.localizedTitle,
|
||||
description: product.localizedDescription,
|
||||
price: product.price.stringValue,
|
||||
priceCurrencyCode: product.priceLocale.currencyCode,
|
||||
priceAmountMicros: Int64(product.price.doubleValue * 1_000_000),
|
||||
productType: product.productIdentifier.contains("monthly")
|
||||
|| product.productIdentifier.contains("yearly") ? "subscription" : "consumable"
|
||||
)
|
||||
}
|
||||
invoke.resolve(["products": productsData])
|
||||
}
|
||||
} catch {
|
||||
invoke.reject("Failed to parse fetch products arguments: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
|
||||
@objc public func iap_purchase_product(_ invoke: Invoke) {
|
||||
do {
|
||||
let args = try invoke.parseArgs(PurchaseProductRequest.self)
|
||||
|
||||
StoreKitManager.shared.fetchProducts(productIds: [args.productId]) { products in
|
||||
guard let product = products.first else {
|
||||
invoke.reject("Product not found")
|
||||
return
|
||||
}
|
||||
|
||||
StoreKitManager.shared.purchase(product: product) { result in
|
||||
switch result {
|
||||
case .success(let txn):
|
||||
let purchase = PurchaseData(
|
||||
productId: txn.payment.productIdentifier,
|
||||
transactionId: txn.transactionIdentifier ?? "",
|
||||
originalTransactionId: txn.original?.transactionIdentifier ?? txn
|
||||
.transactionIdentifier ?? "",
|
||||
purchaseDate: ISO8601DateFormatter().string(from: txn.transactionDate ?? Date()),
|
||||
purchaseState: "purchased",
|
||||
platform: "ios"
|
||||
)
|
||||
invoke.resolve(["purchase": purchase])
|
||||
case .failure(let error):
|
||||
invoke.reject("Purchase failed: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
invoke.reject("Failed to parse purchase arguments: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
|
||||
@objc public func iap_restore_purchases(_ invoke: Invoke) {
|
||||
StoreKitManager.shared.restorePurchases { transactions in
|
||||
let restored = transactions.map { txn -> PurchaseData in
|
||||
return PurchaseData(
|
||||
productId: txn.payment.productIdentifier,
|
||||
transactionId: txn.transactionIdentifier ?? "",
|
||||
originalTransactionId: txn.original?.transactionIdentifier ?? txn.transactionIdentifier
|
||||
?? "",
|
||||
purchaseDate: ISO8601DateFormatter().string(from: txn.transactionDate ?? Date()),
|
||||
purchaseState: "restored",
|
||||
platform: "ios"
|
||||
)
|
||||
}
|
||||
invoke.resolve(["purchases": restored])
|
||||
}
|
||||
}
|
||||
|
||||
@objc public func get_system_color_scheme(_ invoke: Invoke) {
|
||||
let userInterfaceStyle = UITraitCollection.current.userInterfaceStyle
|
||||
let colorScheme = (userInterfaceStyle == .dark) ? "dark" : "light"
|
||||
invoke.resolve(["colorScheme": colorScheme])
|
||||
}
|
||||
}
|
||||
|
||||
@_cdecl("init_plugin_native_bridge")
|
||||
@@ -431,3 +552,66 @@ extension NativeBridgePlugin: ASWebAuthenticationPresentationContextProviding {
|
||||
return UIApplication.shared.windows.first ?? UIWindow()
|
||||
}
|
||||
}
|
||||
|
||||
extension NativeBridgePlugin: UIApplicationDelegate {
|
||||
public func application(
|
||||
_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?
|
||||
) -> UIInterfaceOrientationMask {
|
||||
return self.currentOrientationMask
|
||||
}
|
||||
|
||||
/*
|
||||
Proxy all application delegate methods to the original delegate:
|
||||
sel!(application:didFinishLaunchingWithOptions:),
|
||||
sel!(application:openURL:options:),
|
||||
sel!(application:continue:restorationHandler:),
|
||||
sel!(applicationDidBecomeActive:),
|
||||
sel!(applicationWillResignActive:),
|
||||
sel!(applicationWillEnterForeground:),
|
||||
sel!(applicationDidEnterBackground:),
|
||||
sel!(applicationWillTerminate:),
|
||||
*/
|
||||
|
||||
public func application(
|
||||
_ application: UIApplication,
|
||||
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
||||
) -> Bool {
|
||||
self.originalDelegate?.application?(application, didFinishLaunchingWithOptions: launchOptions)
|
||||
?? false
|
||||
}
|
||||
|
||||
public func application(
|
||||
_ application: UIApplication, open url: URL,
|
||||
options: [UIApplication.OpenURLOptionsKey: Any] = [:]
|
||||
) -> Bool {
|
||||
self.originalDelegate?.application?(application, open: url, options: options) ?? false
|
||||
}
|
||||
|
||||
public func application(
|
||||
_ application: UIApplication, continue continueUserActivity: NSUserActivity,
|
||||
restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
|
||||
) -> Bool {
|
||||
self.originalDelegate?.application?(
|
||||
application, continue: continueUserActivity, restorationHandler: restorationHandler) ?? false
|
||||
}
|
||||
|
||||
public func applicationDidBecomeActive(_ application: UIApplication) {
|
||||
self.originalDelegate?.applicationDidBecomeActive?(application)
|
||||
}
|
||||
|
||||
public func applicationWillResignActive(_ application: UIApplication) {
|
||||
self.originalDelegate?.applicationWillResignActive?(application)
|
||||
}
|
||||
|
||||
public func applicationWillEnterForeground(_ application: UIApplication) {
|
||||
self.originalDelegate?.applicationWillEnterForeground?(application)
|
||||
}
|
||||
|
||||
public func applicationDidEnterBackground(_ application: UIApplication) {
|
||||
self.originalDelegate?.applicationDidEnterBackground?(application)
|
||||
}
|
||||
|
||||
public func applicationWillTerminate(_ application: UIApplication) {
|
||||
self.originalDelegate?.applicationWillTerminate?(application)
|
||||
}
|
||||
}
|
||||
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
import StoreKit
|
||||
import os
|
||||
|
||||
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "StoreKitManager")
|
||||
|
||||
class StoreKitManager: NSObject, SKProductsRequestDelegate, SKPaymentTransactionObserver {
|
||||
static let shared = StoreKitManager()
|
||||
|
||||
private var productsRequest: SKProductsRequest?
|
||||
private var productResponseHandler: (([SKProduct]) -> Void)?
|
||||
private var purchaseHandler: ((Result<SKPaymentTransaction, Error>) -> Void)?
|
||||
private var restoreHandler: (([SKPaymentTransaction]) -> Void)?
|
||||
|
||||
private override init() {
|
||||
super.init()
|
||||
}
|
||||
|
||||
func initialize() {
|
||||
SKPaymentQueue.default().add(self)
|
||||
}
|
||||
|
||||
deinit {
|
||||
SKPaymentQueue.default().remove(self)
|
||||
}
|
||||
|
||||
func fetchProducts(productIds: [String], completion: @escaping ([SKProduct]) -> Void) {
|
||||
let ids = Set(productIds)
|
||||
productsRequest = SKProductsRequest(productIdentifiers: ids)
|
||||
productsRequest?.delegate = self
|
||||
productResponseHandler = completion
|
||||
productsRequest?.start()
|
||||
}
|
||||
|
||||
func purchase(
|
||||
product: SKProduct, completion: @escaping (Result<SKPaymentTransaction, Error>) -> Void
|
||||
) {
|
||||
guard SKPaymentQueue.canMakePayments() else {
|
||||
completion(
|
||||
.failure(
|
||||
NSError(
|
||||
domain: "iap", code: 0, userInfo: [NSLocalizedDescriptionKey: "Purchases disabled."])))
|
||||
return
|
||||
}
|
||||
purchaseHandler = completion
|
||||
let payment = SKPayment(product: product)
|
||||
SKPaymentQueue.default().add(payment)
|
||||
}
|
||||
|
||||
func restorePurchases(completion: @escaping ([SKPaymentTransaction]) -> Void) {
|
||||
restoreHandler = completion
|
||||
SKPaymentQueue.default().restoreCompletedTransactions()
|
||||
}
|
||||
|
||||
// MARK: - SKProductsRequestDelegate
|
||||
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
self?.productResponseHandler?(response.products)
|
||||
self?.productResponseHandler = nil
|
||||
}
|
||||
}
|
||||
|
||||
func request(_ request: SKRequest, didFailWithError error: Error) {
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
logger.error("Products request failed: \(error.localizedDescription)")
|
||||
self?.productResponseHandler?([])
|
||||
self?.productResponseHandler = nil
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - SKPaymentTransactionObserver
|
||||
func paymentQueue(
|
||||
_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]
|
||||
) {
|
||||
for transaction in transactions {
|
||||
switch transaction.transactionState {
|
||||
case .purchased:
|
||||
SKPaymentQueue.default().finishTransaction(transaction)
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
self?.purchaseHandler?(.success(transaction))
|
||||
self?.purchaseHandler = nil
|
||||
}
|
||||
case .failed:
|
||||
SKPaymentQueue.default().finishTransaction(transaction)
|
||||
let error =
|
||||
transaction.error
|
||||
?? NSError(
|
||||
domain: "iap", code: -1, userInfo: [NSLocalizedDescriptionKey: "Unknown purchase error"]
|
||||
)
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
self?.purchaseHandler?(.failure(error))
|
||||
self?.purchaseHandler = nil
|
||||
}
|
||||
case .restored:
|
||||
SKPaymentQueue.default().finishTransaction(transaction)
|
||||
case .deferred, .purchasing:
|
||||
// Handle these states if needed
|
||||
break
|
||||
@unknown default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
|
||||
let restored = queue.transactions.filter { $0.transactionState == .restored }
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
self?.restoreHandler?(restored)
|
||||
self?.restoreHandler = nil
|
||||
}
|
||||
}
|
||||
|
||||
func paymentQueue(
|
||||
_ queue: SKPaymentQueue, restoreCompletedTransactionsFailedWithError error: Error
|
||||
) {
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
logger.error("Restore purchases failed: \(error.localizedDescription)")
|
||||
self?.restoreHandler?([])
|
||||
self?.restoreHandler = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../schemas/schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-get-system-color-scheme"
|
||||
description = "Enables the get_system_color_scheme command without any pre-configured scope."
|
||||
commands.allow = ["get_system_color_scheme"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-get-system-color-scheme"
|
||||
description = "Denies the get_system_color_scheme command without any pre-configured scope."
|
||||
commands.deny = ["get_system_color_scheme"]
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../schemas/schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-iap-fetch-products"
|
||||
description = "Enables the iap_fetch_products command without any pre-configured scope."
|
||||
commands.allow = ["iap_fetch_products"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-iap-fetch-products"
|
||||
description = "Denies the iap_fetch_products command without any pre-configured scope."
|
||||
commands.deny = ["iap_fetch_products"]
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../schemas/schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-iap-initialize"
|
||||
description = "Enables the iap_initialize command without any pre-configured scope."
|
||||
commands.allow = ["iap_initialize"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-iap-initialize"
|
||||
description = "Denies the iap_initialize command without any pre-configured scope."
|
||||
commands.deny = ["iap_initialize"]
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../schemas/schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-iap-purchase-product"
|
||||
description = "Enables the iap_purchase_product command without any pre-configured scope."
|
||||
commands.allow = ["iap_purchase_product"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-iap-purchase-product"
|
||||
description = "Denies the iap_purchase_product command without any pre-configured scope."
|
||||
commands.deny = ["iap_purchase_product"]
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../schemas/schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-iap-restore-purchases"
|
||||
description = "Enables the iap_restore_purchases command without any pre-configured scope."
|
||||
commands.allow = ["iap_restore_purchases"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-iap-restore-purchases"
|
||||
description = "Denies the iap_restore_purchases command without any pre-configured scope."
|
||||
commands.deny = ["iap_restore_purchases"]
|
||||
+135
@@ -14,6 +14,11 @@ Default permissions for the plugin
|
||||
- `allow-get-sys-fonts-list`
|
||||
- `allow-intercept-keys`
|
||||
- `allow-lock-screen-orientation`
|
||||
- `allow-iap-initialize`
|
||||
- `allow-iap-fetch-products`
|
||||
- `allow-iap-purchase-product`
|
||||
- `allow-iap-restore-purchases`
|
||||
- `allow-get-system-color-scheme`
|
||||
|
||||
## Permission Table
|
||||
|
||||
@@ -157,6 +162,136 @@ Denies the get_sys_fonts_list command without any pre-configured scope.
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`native-bridge:allow-get-system-color-scheme`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Enables the get_system_color_scheme command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`native-bridge:deny-get-system-color-scheme`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Denies the get_system_color_scheme command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`native-bridge:allow-iap-fetch-products`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Enables the iap_fetch_products command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`native-bridge:deny-iap-fetch-products`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Denies the iap_fetch_products command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`native-bridge:allow-iap-initialize`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Enables the iap_initialize command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`native-bridge:deny-iap-initialize`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Denies the iap_initialize command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`native-bridge:allow-iap-purchase-product`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Enables the iap_purchase_product command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`native-bridge:deny-iap-purchase-product`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Denies the iap_purchase_product command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`native-bridge:allow-iap-restore-purchases`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Enables the iap_restore_purchases command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`native-bridge:deny-iap-restore-purchases`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Denies the iap_restore_purchases command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`native-bridge:allow-install-package`
|
||||
|
||||
</td>
|
||||
|
||||
@@ -11,4 +11,9 @@ permissions = [
|
||||
"allow-get-sys-fonts-list",
|
||||
"allow-intercept-keys",
|
||||
"allow-lock-screen-orientation",
|
||||
"allow-iap-initialize",
|
||||
"allow-iap-fetch-products",
|
||||
"allow-iap-purchase-product",
|
||||
"allow-iap-restore-purchases",
|
||||
"allow-get-system-color-scheme",
|
||||
]
|
||||
|
||||
+62
-2
@@ -354,6 +354,66 @@
|
||||
"const": "deny-get-sys-fonts-list",
|
||||
"markdownDescription": "Denies the get_sys_fonts_list command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Enables the get_system_color_scheme command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "allow-get-system-color-scheme",
|
||||
"markdownDescription": "Enables the get_system_color_scheme command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the get_system_color_scheme command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deny-get-system-color-scheme",
|
||||
"markdownDescription": "Denies the get_system_color_scheme command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Enables the iap_fetch_products command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "allow-iap-fetch-products",
|
||||
"markdownDescription": "Enables the iap_fetch_products command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the iap_fetch_products command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deny-iap-fetch-products",
|
||||
"markdownDescription": "Denies the iap_fetch_products command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Enables the iap_initialize command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "allow-iap-initialize",
|
||||
"markdownDescription": "Enables the iap_initialize command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the iap_initialize command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deny-iap-initialize",
|
||||
"markdownDescription": "Denies the iap_initialize command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Enables the iap_purchase_product command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "allow-iap-purchase-product",
|
||||
"markdownDescription": "Enables the iap_purchase_product command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the iap_purchase_product command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deny-iap-purchase-product",
|
||||
"markdownDescription": "Denies the iap_purchase_product command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Enables the iap_restore_purchases command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "allow-iap-restore-purchases",
|
||||
"markdownDescription": "Enables the iap_restore_purchases command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the iap_restore_purchases command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deny-iap-restore-purchases",
|
||||
"markdownDescription": "Denies the iap_restore_purchases command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Enables the install_package command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
@@ -415,10 +475,10 @@
|
||||
"markdownDescription": "Denies the use_background_audio command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Default permissions for the plugin\n#### This default permission set includes:\n\n- `allow-auth-with-safari`\n- `allow-auth-with-custom-tab`\n- `allow-copy-uri-to-path`\n- `allow-use-background-audio`\n- `allow-install-package`\n- `allow-set-system-ui-visibility`\n- `allow-get-status-bar-height`\n- `allow-get-sys-fonts-list`\n- `allow-intercept-keys`\n- `allow-lock-screen-orientation`",
|
||||
"description": "Default permissions for the plugin\n#### This default permission set includes:\n\n- `allow-auth-with-safari`\n- `allow-auth-with-custom-tab`\n- `allow-copy-uri-to-path`\n- `allow-use-background-audio`\n- `allow-install-package`\n- `allow-set-system-ui-visibility`\n- `allow-get-status-bar-height`\n- `allow-get-sys-fonts-list`\n- `allow-intercept-keys`\n- `allow-lock-screen-orientation`\n- `allow-iap-initialize`\n- `allow-iap-fetch-products`\n- `allow-iap-purchase-product`\n- `allow-iap-restore-purchases`\n- `allow-get-system-color-scheme`",
|
||||
"type": "string",
|
||||
"const": "default",
|
||||
"markdownDescription": "Default permissions for the plugin\n#### This default permission set includes:\n\n- `allow-auth-with-safari`\n- `allow-auth-with-custom-tab`\n- `allow-copy-uri-to-path`\n- `allow-use-background-audio`\n- `allow-install-package`\n- `allow-set-system-ui-visibility`\n- `allow-get-status-bar-height`\n- `allow-get-sys-fonts-list`\n- `allow-intercept-keys`\n- `allow-lock-screen-orientation`"
|
||||
"markdownDescription": "Default permissions for the plugin\n#### This default permission set includes:\n\n- `allow-auth-with-safari`\n- `allow-auth-with-custom-tab`\n- `allow-copy-uri-to-path`\n- `allow-use-background-audio`\n- `allow-install-package`\n- `allow-set-system-ui-visibility`\n- `allow-get-status-bar-height`\n- `allow-get-sys-fonts-list`\n- `allow-intercept-keys`\n- `allow-lock-screen-orientation`\n- `allow-iap-initialize`\n- `allow-iap-fetch-products`\n- `allow-iap-purchase-product`\n- `allow-iap-restore-purchases`\n- `allow-get-system-color-scheme`"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -81,3 +81,41 @@ pub(crate) async fn lock_screen_orientation<R: Runtime>(
|
||||
) -> Result<()> {
|
||||
app.native_bridge().lock_screen_orientation(payload)
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub(crate) async fn iap_initialize<R: Runtime>(
|
||||
app: AppHandle<R>,
|
||||
payload: IAPInitializeRequest,
|
||||
) -> Result<IAPInitializeResponse> {
|
||||
app.native_bridge().iap_initialize(payload)
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub(crate) async fn iap_fetch_products<R: Runtime>(
|
||||
app: AppHandle<R>,
|
||||
payload: IAPFetchProductsRequest,
|
||||
) -> Result<IAPFetchProductsResponse> {
|
||||
app.native_bridge().iap_fetch_products(payload)
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub(crate) async fn iap_purchase_product<R: Runtime>(
|
||||
app: AppHandle<R>,
|
||||
payload: IAPPurchaseProductRequest,
|
||||
) -> Result<IAPPurchaseProductResponse> {
|
||||
app.native_bridge().iap_purchase_product(payload)
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub(crate) async fn iap_restore_purchases<R: Runtime>(
|
||||
app: AppHandle<R>,
|
||||
) -> Result<IAPRestorePurchasesResponse> {
|
||||
app.native_bridge().iap_restore_purchases()
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub(crate) async fn get_system_color_scheme<R: Runtime>(
|
||||
app: AppHandle<R>,
|
||||
) -> Result<GetSystemColorSchemeResponse> {
|
||||
app.native_bridge().get_system_color_scheme()
|
||||
}
|
||||
|
||||
@@ -73,4 +73,33 @@ impl<R: Runtime> NativeBridge<R> {
|
||||
) -> crate::Result<()> {
|
||||
Err(crate::Error::UnsupportedPlatformError)
|
||||
}
|
||||
|
||||
pub fn iap_initialize(
|
||||
&self,
|
||||
_payload: IAPInitializeRequest,
|
||||
) -> crate::Result<IAPInitializeResponse> {
|
||||
Err(crate::Error::UnsupportedPlatformError)
|
||||
}
|
||||
|
||||
pub fn iap_fetch_products(
|
||||
&self,
|
||||
_payload: IAPFetchProductsRequest,
|
||||
) -> crate::Result<IAPFetchProductsResponse> {
|
||||
Err(crate::Error::UnsupportedPlatformError)
|
||||
}
|
||||
|
||||
pub fn iap_purchase_product(
|
||||
&self,
|
||||
_payload: IAPPurchaseProductRequest,
|
||||
) -> crate::Result<IAPPurchaseProductResponse> {
|
||||
Err(crate::Error::UnsupportedPlatformError)
|
||||
}
|
||||
|
||||
pub fn iap_restore_purchases(&self) -> crate::Result<IAPRestorePurchasesResponse> {
|
||||
Err(crate::Error::UnsupportedPlatformError)
|
||||
}
|
||||
|
||||
pub fn get_system_color_scheme(&self) -> crate::Result<GetSystemColorSchemeResponse> {
|
||||
Err(crate::Error::UnsupportedPlatformError)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,11 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
||||
commands::get_sys_fonts_list,
|
||||
commands::intercept_keys,
|
||||
commands::lock_screen_orientation,
|
||||
commands::iap_initialize,
|
||||
commands::iap_fetch_products,
|
||||
commands::iap_purchase_product,
|
||||
commands::iap_restore_purchases,
|
||||
commands::get_system_color_scheme,
|
||||
])
|
||||
.setup(|app, api| {
|
||||
#[cfg(mobile)]
|
||||
|
||||
@@ -112,3 +112,52 @@ impl<R: Runtime> NativeBridge<R> {
|
||||
.map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Runtime> NativeBridge<R> {
|
||||
pub fn iap_initialize(
|
||||
&self,
|
||||
payload: IAPInitializeRequest,
|
||||
) -> crate::Result<IAPInitializeResponse> {
|
||||
self.0
|
||||
.run_mobile_plugin("iap_initialize", payload)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Runtime> NativeBridge<R> {
|
||||
pub fn iap_fetch_products(
|
||||
&self,
|
||||
payload: IAPFetchProductsRequest,
|
||||
) -> crate::Result<IAPFetchProductsResponse> {
|
||||
self.0
|
||||
.run_mobile_plugin("iap_fetch_products", payload)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Runtime> NativeBridge<R> {
|
||||
pub fn iap_purchase_product(
|
||||
&self,
|
||||
payload: IAPPurchaseProductRequest,
|
||||
) -> crate::Result<IAPPurchaseProductResponse> {
|
||||
self.0
|
||||
.run_mobile_plugin("iap_purchase_product", payload)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Runtime> NativeBridge<R> {
|
||||
pub fn iap_restore_purchases(&self) -> crate::Result<IAPRestorePurchasesResponse> {
|
||||
self.0
|
||||
.run_mobile_plugin("iap_restore_purchases", ())
|
||||
.map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Runtime> NativeBridge<R> {
|
||||
pub fn get_system_color_scheme(&self) -> crate::Result<GetSystemColorSchemeResponse> {
|
||||
self.0
|
||||
.run_mobile_plugin("get_system_color_scheme", ())
|
||||
.map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,3 +86,75 @@ pub struct InterceptKeysRequest {
|
||||
pub struct LockScreenOrientationRequest {
|
||||
pub orientation: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Product {
|
||||
pub id: String,
|
||||
pub title: String,
|
||||
pub description: String,
|
||||
pub price: String,
|
||||
pub price_currency_code: Option<String>,
|
||||
pub price_amount_micros: i64,
|
||||
pub product_type: String, // "consumable", "non_consumable", or "subscription"
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Purchase {
|
||||
pub product_id: String,
|
||||
pub transaction_id: String,
|
||||
pub purchase_date: String,
|
||||
pub original_transaction_id: String,
|
||||
pub purchase_state: String, // "purchased", "pending", "cancelled"
|
||||
pub platform: String, // "ios" or "android"
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct IAPInitializeRequest {
|
||||
pub public_key: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct IAPInitializeResponse {
|
||||
pub success: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct IAPFetchProductsRequest {
|
||||
pub product_ids: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct IAPFetchProductsResponse {
|
||||
pub products: Vec<Product>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct IAPPurchaseProductRequest {
|
||||
pub product_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct IAPPurchaseProductResponse {
|
||||
pub purchase: Option<Purchase>,
|
||||
pub cancelled_purchase: Option<Purchase>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct IAPRestorePurchasesResponse {
|
||||
pub purchases: Vec<Purchase>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct GetSystemColorSchemeResponse {
|
||||
pub color_scheme: String, // "light" or "dark"
|
||||
}
|
||||
|
||||
@@ -9,11 +9,11 @@ exclude = ["/examples", "/dist-js", "/guest-js", "/node_modules"]
|
||||
links = "tauri-plugin-native-tts"
|
||||
|
||||
[dependencies]
|
||||
tauri = { version = "2.5.0" }
|
||||
tauri = { version = "2" }
|
||||
serde = "1.0"
|
||||
thiserror = "2"
|
||||
schemars = "0.8"
|
||||
|
||||
[build-dependencies]
|
||||
tauri-plugin = { version = "2.2.0", features = ["build"] }
|
||||
tauri-plugin = { version = "2", features = ["build"] }
|
||||
schemars = "0.8"
|
||||
|
||||
@@ -5,7 +5,7 @@ plugins {
|
||||
|
||||
android {
|
||||
namespace = "com.readest.native_tts"
|
||||
compileSdk = 34
|
||||
compileSdk = 36
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 21
|
||||
|
||||
+5
@@ -1,3 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<queries>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.TTS_SERVICE" />
|
||||
</intent>
|
||||
</queries>
|
||||
</manifest>
|
||||
|
||||
+7
-2
@@ -3,6 +3,7 @@ package com.readest.native_tts
|
||||
import android.os.Bundle
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.provider.Settings
|
||||
import android.speech.tts.TextToSpeech
|
||||
import android.speech.tts.UtteranceProgressListener
|
||||
import android.speech.tts.Voice
|
||||
@@ -97,7 +98,11 @@ class NativeTTSPlugin(private val activity: Activity) : Plugin(activity) {
|
||||
|
||||
private suspend fun initializeTTS(): Boolean = suspendCancellableCoroutine { continuation ->
|
||||
try {
|
||||
textToSpeech = TextToSpeech(activity) { status ->
|
||||
val preferredEngine = Settings.Secure.getString(
|
||||
activity.contentResolver,
|
||||
Settings.Secure.TTS_DEFAULT_SYNTH
|
||||
)
|
||||
textToSpeech = TextToSpeech(activity, { status ->
|
||||
when (status) {
|
||||
TextToSpeech.SUCCESS -> {
|
||||
setupTTSListener()
|
||||
@@ -111,7 +116,7 @@ class NativeTTSPlugin(private val activity: Activity) : Plugin(activity) {
|
||||
continuation.resume(false) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}, preferredEngine)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Exception during TTS initialization", e)
|
||||
@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
|
||||
|
||||
@@ -29,14 +29,14 @@ fn allow_file_in_scopes(app: &AppHandle, files: Vec<PathBuf>) {
|
||||
let asset_protocol_scope = app.asset_protocol_scope();
|
||||
for file in &files {
|
||||
if let Err(e) = fs_scope.allow_file(file) {
|
||||
eprintln!("Failed to allow file in fs_scope: {}", e);
|
||||
eprintln!("Failed to allow file in fs_scope: {e}");
|
||||
} else {
|
||||
println!("Allowed file in fs_scope: {:?}", file);
|
||||
println!("Allowed file in fs_scope: {file:?}");
|
||||
}
|
||||
if let Err(e) = asset_protocol_scope.allow_file(file) {
|
||||
eprintln!("Failed to allow file in asset_protocol_scope: {}", e);
|
||||
eprintln!("Failed to allow file in asset_protocol_scope: {e}");
|
||||
} else {
|
||||
println!("Allowed file in asset_protocol_scope: {:?}", file);
|
||||
println!("Allowed file in asset_protocol_scope: {file:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -80,18 +80,18 @@ fn set_window_open_with_files(app: &AppHandle, files: Vec<PathBuf>) {
|
||||
.collect::<Vec<_>>()
|
||||
.join(",");
|
||||
let window = app.get_webview_window("main").unwrap();
|
||||
let script = format!("window.OPEN_WITH_FILES = [{}];", files);
|
||||
let script = format!("window.OPEN_WITH_FILES = [{files}];");
|
||||
if let Err(e) = window.eval(&script) {
|
||||
eprintln!("Failed to set open files variable: {}", e);
|
||||
eprintln!("Failed to set open files variable: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(desktop)]
|
||||
fn set_rounded_window(app: &AppHandle, rounded: bool) {
|
||||
let window = app.get_webview_window("main").unwrap();
|
||||
let script = format!("window.IS_ROUNDED = {};", rounded);
|
||||
let script = format!("window.IS_ROUNDED = {rounded};");
|
||||
if let Err(e) = window.eval(&script) {
|
||||
eprintln!("Failed to set IS_ROUNDED variable: {}", e);
|
||||
eprintln!("Failed to set IS_ROUNDED variable: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,6 +105,11 @@ async fn start_server(window: Window) -> Result<u16, String> {
|
||||
.map_err(|err| err.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn get_environment_variable(name: &str) -> String {
|
||||
std::env::var(String::from(name)).unwrap_or(String::from(""))
|
||||
}
|
||||
|
||||
#[derive(Clone, serde::Serialize)]
|
||||
#[allow(dead_code)]
|
||||
struct Payload {
|
||||
@@ -121,6 +126,7 @@ pub fn run() {
|
||||
start_server,
|
||||
download_file,
|
||||
upload_file,
|
||||
get_environment_variable,
|
||||
#[cfg(target_os = "macos")]
|
||||
macos::safari_auth::auth_with_safari,
|
||||
#[cfg(target_os = "macos")]
|
||||
@@ -220,28 +226,10 @@ pub fn run() {
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
{
|
||||
use tauri_plugin_deep_link::DeepLinkExt;
|
||||
app.deep_link().register_all()?;
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
fn has_xdg_mime() -> bool {
|
||||
std::process::Command::new("which")
|
||||
.arg("xdg-mime")
|
||||
.output()
|
||||
.map(|output| output.status.success())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
if has_xdg_mime() {
|
||||
use tauri_plugin_deep_link::DeepLinkExt;
|
||||
app.deep_link().register_all()?;
|
||||
} else {
|
||||
println!("xdg-mime not found; skipping deep link setup on Linux.");
|
||||
}
|
||||
let _ = app.deep_link().register_all();
|
||||
}
|
||||
|
||||
if let Err(e) = app.handle().plugin(
|
||||
@@ -249,7 +237,7 @@ pub fn run() {
|
||||
.level(log::LevelFilter::Info)
|
||||
.build(),
|
||||
) {
|
||||
eprintln!("Failed to initialize tauri_plugin_log: {}", e);
|
||||
eprintln!("Failed to initialize tauri_plugin_log: {e}");
|
||||
};
|
||||
|
||||
let win_builder = WebviewWindowBuilder::new(app, "main", WebviewUrl::default());
|
||||
@@ -265,25 +253,22 @@ pub fn run() {
|
||||
|
||||
#[cfg(all(not(target_os = "macos"), desktop))]
|
||||
let win_builder = {
|
||||
let mut win_builder = win_builder
|
||||
let mut builder = win_builder
|
||||
.decorations(false)
|
||||
.transparent(true)
|
||||
.visible(false)
|
||||
.shadow(true)
|
||||
.title("Readest");
|
||||
|
||||
if cfg!(target_os = "windows") {
|
||||
if tauri_plugin_os::version()
|
||||
<= tauri_plugin_os::Version::from_string("10.0.19045")
|
||||
{
|
||||
win_builder = win_builder.shadow(false);
|
||||
} else {
|
||||
win_builder = win_builder.shadow(true);
|
||||
}
|
||||
} else {
|
||||
win_builder = win_builder.shadow(true);
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
builder = builder.transparent(false);
|
||||
}
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
builder = builder.transparent(true);
|
||||
}
|
||||
|
||||
win_builder
|
||||
builder
|
||||
};
|
||||
|
||||
win_builder.build().unwrap();
|
||||
|
||||
@@ -144,7 +144,8 @@ pub fn setup_traffic_light_positioner<R: Runtime>(window: Window<R>) {
|
||||
.expect("NS window should exist on state to handle resize")
|
||||
as id;
|
||||
|
||||
if state.window.label() == "main" {
|
||||
if state.window.label() == "main" || state.window.label().starts_with("reader")
|
||||
{
|
||||
position_traffic_lights(
|
||||
UnsafeWindowHandle(id as *mut std::ffi::c_void),
|
||||
TRAFFIC_LIGHTS_VISIBLE,
|
||||
@@ -278,7 +279,8 @@ pub fn setup_traffic_light_positioner<R: Runtime>(window: Window<R>) {
|
||||
.expect("Failed to emit event");
|
||||
|
||||
let id = state.window.ns_window().expect("Failed to emit event") as id;
|
||||
if state.window.label() == "main" {
|
||||
if state.window.label() == "main" || state.window.label().starts_with("reader")
|
||||
{
|
||||
position_traffic_lights(
|
||||
UnsafeWindowHandle(id as *mut std::ffi::c_void),
|
||||
TRAFFIC_LIGHTS_VISIBLE,
|
||||
@@ -363,26 +365,26 @@ pub fn setup_traffic_light_positioner<R: Runtime>(window: Window<R>) {
|
||||
app_box: *mut c_void = app_box,
|
||||
toolbar: id = cocoa::base::nil,
|
||||
super_delegate: id = current_delegate,
|
||||
(windowShouldClose:) => on_window_should_close as extern fn(&Object, Sel, id) -> BOOL,
|
||||
(windowWillClose:) => on_window_will_close as extern fn(&Object, Sel, id),
|
||||
(windowDidResize:) => on_window_did_resize::<R> as extern fn(&Object, Sel, id),
|
||||
(windowDidMove:) => on_window_did_move as extern fn(&Object, Sel, id),
|
||||
(windowDidChangeBackingProperties:) => on_window_did_change_backing_properties as extern fn(&Object, Sel, id),
|
||||
(windowDidBecomeKey:) => on_window_did_become_key as extern fn(&Object, Sel, id),
|
||||
(windowDidResignKey:) => on_window_did_resign_key as extern fn(&Object, Sel, id),
|
||||
(draggingEntered:) => on_dragging_entered as extern fn(&Object, Sel, id) -> BOOL,
|
||||
(prepareForDragOperation:) => on_prepare_for_drag_operation as extern fn(&Object, Sel, id) -> BOOL,
|
||||
(performDragOperation:) => on_perform_drag_operation as extern fn(&Object, Sel, id) -> BOOL,
|
||||
(concludeDragOperation:) => on_conclude_drag_operation as extern fn(&Object, Sel, id),
|
||||
(draggingExited:) => on_dragging_exited as extern fn(&Object, Sel, id),
|
||||
(window:willUseFullScreenPresentationOptions:) => on_window_will_use_full_screen_presentation_options as extern fn(&Object, Sel, id, NSUInteger) -> NSUInteger,
|
||||
(windowDidEnterFullScreen:) => on_window_did_enter_full_screen::<R> as extern fn(&Object, Sel, id),
|
||||
(windowWillEnterFullScreen:) => on_window_will_enter_full_screen::<R> as extern fn(&Object, Sel, id),
|
||||
(windowDidExitFullScreen:) => on_window_did_exit_full_screen::<R> as extern fn(&Object, Sel, id),
|
||||
(windowWillExitFullScreen:) => on_window_will_exit_full_screen::<R> as extern fn(&Object, Sel, id),
|
||||
(windowDidFailToEnterFullScreen:) => on_window_did_fail_to_enter_full_screen as extern fn(&Object, Sel, id),
|
||||
(effectiveAppearanceDidChange:) => on_effective_appearance_did_change as extern fn(&Object, Sel, id),
|
||||
(effectiveAppearanceDidChangedOnMainThread:) => on_effective_appearance_did_changed_on_main_thread as extern fn(&Object, Sel, id)
|
||||
(windowShouldClose:) => on_window_should_close as extern "C" fn(&Object, Sel, id) -> BOOL,
|
||||
(windowWillClose:) => on_window_will_close as extern "C" fn(&Object, Sel, id),
|
||||
(windowDidResize:) => on_window_did_resize::<R> as extern "C" fn(&Object, Sel, id),
|
||||
(windowDidMove:) => on_window_did_move as extern "C" fn(&Object, Sel, id),
|
||||
(windowDidChangeBackingProperties:) => on_window_did_change_backing_properties as extern "C" fn(&Object, Sel, id),
|
||||
(windowDidBecomeKey:) => on_window_did_become_key as extern "C" fn(&Object, Sel, id),
|
||||
(windowDidResignKey:) => on_window_did_resign_key as extern "C" fn(&Object, Sel, id),
|
||||
(draggingEntered:) => on_dragging_entered as extern "C" fn(&Object, Sel, id) -> BOOL,
|
||||
(prepareForDragOperation:) => on_prepare_for_drag_operation as extern "C" fn(&Object, Sel, id) -> BOOL,
|
||||
(performDragOperation:) => on_perform_drag_operation as extern "C" fn(&Object, Sel, id) -> BOOL,
|
||||
(concludeDragOperation:) => on_conclude_drag_operation as extern "C" fn(&Object, Sel, id),
|
||||
(draggingExited:) => on_dragging_exited as extern "C" fn(&Object, Sel, id),
|
||||
(window:willUseFullScreenPresentationOptions:) => on_window_will_use_full_screen_presentation_options as extern "C" fn(&Object, Sel, id, NSUInteger) -> NSUInteger,
|
||||
(windowDidEnterFullScreen:) => on_window_did_enter_full_screen::<R> as extern "C" fn(&Object, Sel, id),
|
||||
(windowWillEnterFullScreen:) => on_window_will_enter_full_screen::<R> as extern "C" fn(&Object, Sel, id),
|
||||
(windowDidExitFullScreen:) => on_window_did_exit_full_screen::<R> as extern "C" fn(&Object, Sel, id),
|
||||
(windowWillExitFullScreen:) => on_window_will_exit_full_screen::<R> as extern "C" fn(&Object, Sel, id),
|
||||
(windowDidFailToEnterFullScreen:) => on_window_did_fail_to_enter_full_screen as extern "C" fn(&Object, Sel, id),
|
||||
(effectiveAppearanceDidChange:) => on_effective_appearance_did_change as extern "C" fn(&Object, Sel, id),
|
||||
(effectiveAppearanceDidChangedOnMainThread:) => on_effective_appearance_did_changed_on_main_thread as extern "C" fn(&Object, Sel, id)
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,8 +17,8 @@ use tokio_util::codec::{BytesCodec, FramedRead};
|
||||
|
||||
use read_progress_stream::ReadProgressStream;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::time::Instant;
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
@@ -109,41 +109,129 @@ pub async fn download_file(
|
||||
body: Option<String>,
|
||||
on_progress: Channel<ProgressPayload>,
|
||||
) -> Result<()> {
|
||||
use futures::stream::{self, StreamExt};
|
||||
use std::cmp::min;
|
||||
use tokio::io::AsyncSeekExt;
|
||||
|
||||
const PART_SIZE: u64 = 1024 * 1024;
|
||||
|
||||
let client = reqwest::Client::new();
|
||||
let mut request = if let Some(body) = body {
|
||||
client.post(url).body(body)
|
||||
} else {
|
||||
client.get(url)
|
||||
};
|
||||
// Loop trought the headers keys and values
|
||||
// and add them to the request object.
|
||||
for (key, value) in headers {
|
||||
request = request.header(&key, value);
|
||||
|
||||
// Check if server supports range requests
|
||||
let range_resp = client.get(url).header("Range", "bytes=0-0").send().await?;
|
||||
let accept_ranges = range_resp
|
||||
.headers()
|
||||
.get("accept-ranges")
|
||||
.map(|v| v.to_str().unwrap_or(""))
|
||||
.unwrap_or("")
|
||||
.eq_ignore_ascii_case("bytes");
|
||||
let total = range_resp
|
||||
.headers()
|
||||
.get("content-range")
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.and_then(|s| s.split('/').nth(1))
|
||||
.and_then(|s| s.parse::<u64>().ok())
|
||||
.unwrap_or(0);
|
||||
|
||||
if !accept_ranges || total == 0 {
|
||||
// Fallback to original single-threaded logic
|
||||
let mut request = if let Some(body) = body {
|
||||
client.post(url).body(body)
|
||||
} else {
|
||||
client.get(url)
|
||||
};
|
||||
|
||||
for (key, value) in headers.iter() {
|
||||
request = request.header(key, value);
|
||||
}
|
||||
|
||||
let response = request.send().await?;
|
||||
if !response.status().is_success() {
|
||||
return Err(Error::HttpErrorCode(
|
||||
response.status().as_u16(),
|
||||
response.text().await.unwrap_or_default(),
|
||||
));
|
||||
}
|
||||
|
||||
let total = response.content_length().unwrap_or(0);
|
||||
let mut file = BufWriter::new(File::create(file_path).await?);
|
||||
let mut stream = response.bytes_stream();
|
||||
|
||||
let mut stats = TransferStats::default();
|
||||
while let Some(chunk) = stream.try_next().await? {
|
||||
file.write_all(&chunk).await?;
|
||||
stats.record_chunk_transfer(chunk.len());
|
||||
let _ = on_progress.send(ProgressPayload {
|
||||
progress: stats.total_transferred,
|
||||
total,
|
||||
transfer_speed: stats.transfer_speed,
|
||||
});
|
||||
}
|
||||
file.flush().await?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let response = request.send().await?;
|
||||
if !response.status().is_success() {
|
||||
return Err(Error::HttpErrorCode(
|
||||
response.status().as_u16(),
|
||||
response.text().await.unwrap_or_default(),
|
||||
));
|
||||
}
|
||||
let total = response.content_length().unwrap_or(0);
|
||||
// Multi-part download with range access
|
||||
let part_count = total.div_ceil(PART_SIZE);
|
||||
let file = File::create(file_path).await?;
|
||||
file.set_len(total).await?;
|
||||
|
||||
let mut file = BufWriter::new(File::create(file_path).await?);
|
||||
let mut stream = response.bytes_stream();
|
||||
let file = Arc::new(tokio::sync::Mutex::new(file));
|
||||
let progress = Arc::new(tokio::sync::Mutex::new(TransferStats::default()));
|
||||
|
||||
let mut stats = TransferStats::default();
|
||||
while let Some(chunk) = stream.try_next().await? {
|
||||
file.write_all(&chunk).await?;
|
||||
stats.record_chunk_transfer(chunk.len());
|
||||
let _ = on_progress.send(ProgressPayload {
|
||||
progress: stats.total_transferred,
|
||||
total,
|
||||
transfer_speed: stats.transfer_speed,
|
||||
});
|
||||
}
|
||||
file.flush().await?;
|
||||
stream::iter(0..part_count)
|
||||
.for_each_concurrent(8, |i| {
|
||||
let client = client.clone();
|
||||
let file = Arc::clone(&file);
|
||||
let progress = Arc::clone(&progress);
|
||||
let headers = headers.clone();
|
||||
let url = url.to_string();
|
||||
let on_progress = on_progress.clone();
|
||||
|
||||
async move {
|
||||
let start = i * PART_SIZE;
|
||||
let end = min(start + PART_SIZE - 1, total - 1);
|
||||
let range_header = format!("bytes={start}-{end}");
|
||||
|
||||
let mut req = client.get(&url).header("Range", range_header);
|
||||
for (key, value) in headers {
|
||||
req = req.header(key, value);
|
||||
}
|
||||
|
||||
let resp = match req.send().await {
|
||||
Ok(r) => r,
|
||||
Err(_) => return,
|
||||
};
|
||||
|
||||
if !resp.status().is_success()
|
||||
&& resp.status() != reqwest::StatusCode::PARTIAL_CONTENT
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let bytes = match resp.bytes().await {
|
||||
Ok(b) => b,
|
||||
Err(_) => return,
|
||||
};
|
||||
|
||||
{
|
||||
let mut f = file.lock().await;
|
||||
f.seek(std::io::SeekFrom::Start(start)).await.unwrap();
|
||||
f.write_all(&bytes).await.unwrap();
|
||||
}
|
||||
|
||||
{
|
||||
let mut stat = progress.lock().await;
|
||||
stat.record_chunk_transfer(bytes.len());
|
||||
let _ = on_progress.send(ProgressPayload {
|
||||
progress: stat.total_transferred,
|
||||
total,
|
||||
transfer_speed: stat.transfer_speed,
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -15,12 +15,12 @@
|
||||
"security": {
|
||||
"csp": {
|
||||
"default-src": "'self' 'unsafe-inline' blob: data: customprotocol: asset: http://asset.localhost ipc: http://ipc.localhost",
|
||||
"connect-src": "'self' blob: data: asset: http://asset.localhost ipc: http://ipc.localhost https://*.sentry.io https://*.posthog.com https://*.deepl.com https://*.wikipedia.org https://*.wiktionary.org https://*.supabase.co https://*.readest.com wss://speech.platform.bing.com https://*.cloudflarestorage.com https://translate.googleapis.com https://*.microsofttranslator.com https://edge.microsoft.com",
|
||||
"connect-src": "'self' blob: data: asset: http://asset.localhost ipc: http://ipc.localhost https://*.sentry.io https://*.posthog.com https://*.deepl.com https://*.wikipedia.org https://*.wiktionary.org https://*.supabase.co https://*.readest.com wss://speech.platform.bing.com https://*.cloudflarestorage.com https://translate.googleapis.com https://translate.toil.cc https://*.microsofttranslator.com https://edge.microsoft.com https://*.googleusercontent.com",
|
||||
"img-src": "'self' blob: data: asset: http://asset.localhost https://*",
|
||||
"style-src": "'self' 'unsafe-inline' blob: asset: http://asset.localhost https://cdn.jsdelivr.net https://fonts.googleapis.com https://fontsapi.zeoseven.com",
|
||||
"font-src": "'self' blob: data: asset: http://asset.localhost tauri: https://db.onlinewebfonts.com https://cdn.jsdelivr.net https://fonts.gstatic.com https://fontsapi.zeoseven.com",
|
||||
"frame-src": "'self' blob: asset: http://asset.localhost",
|
||||
"script-src": "'self' 'unsafe-inline' 'unsafe-eval' blob: asset: http://asset.localhost https://*.sentry.io https://*.posthog.com"
|
||||
"style-src": "'self' 'unsafe-inline' blob: asset: http://asset.localhost https://cdn.jsdelivr.net https://fonts.googleapis.com https://chinese-fonts-cdn.netlify.app",
|
||||
"font-src": "'self' blob: data: asset: http://asset.localhost tauri: https://db.onlinewebfonts.com https://cdn.jsdelivr.net https://fonts.gstatic.com https://chinese-fonts-cdn.netlify.app",
|
||||
"frame-src": "'self' blob: asset: http://asset.localhost https://*.stripe.com",
|
||||
"script-src": "'self' 'unsafe-inline' 'unsafe-eval' blob: asset: http://asset.localhost https://*.sentry.io https://*.posthog.com https://*.stripe.com"
|
||||
},
|
||||
"assetProtocol": {
|
||||
"enable": true,
|
||||
@@ -28,6 +28,7 @@
|
||||
"allow": [
|
||||
"$RESOURCE/**",
|
||||
"$APPDATA/**/*",
|
||||
"$APPCACHE/**/*",
|
||||
"$TEMP/**/*",
|
||||
"/private/var/mobile/Containers/Data/Application/**/*"
|
||||
],
|
||||
@@ -151,7 +152,10 @@
|
||||
},
|
||||
"updater": {
|
||||
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEJFMEQ1QjE2OEU1NEIzNTEKUldSUnMxU09GbHNOdmpEaWFMT1crRFpEV2VORzQ2MklxaFc0M1R0ci9xY2c1bENXS0xhM1R1L2sK",
|
||||
"endpoints": ["https://github.com/readest/readest/releases/latest/download/latest.json"]
|
||||
"endpoints": [
|
||||
"https://download.readest.com/releases/latest.json",
|
||||
"https://github.com/readest/readest/releases/latest/download/latest.json"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { POST } from '@/app/api/apple/iap-verify/route';
|
||||
import { NextRequest } from 'next/server';
|
||||
import { setupSupabaseMocks } from '../helpers/supabase-mock';
|
||||
|
||||
const SKIP_IAP_API_TESTS = !process.env['ENABLE_IAP_API_TESTS'];
|
||||
vi.mock('@/utils/supabase', () => ({
|
||||
supabase: {
|
||||
auth: {
|
||||
getUser: vi.fn(),
|
||||
refreshSession: vi.fn(),
|
||||
},
|
||||
from: vi.fn(),
|
||||
},
|
||||
createSupabaseAdminClient: vi.fn(),
|
||||
}));
|
||||
|
||||
describe.skipIf(SKIP_IAP_API_TESTS)('/api/apple/iap-verify', () => {
|
||||
it('should verify a valid transaction', async () => {
|
||||
setupSupabaseMocks();
|
||||
const request = new NextRequest('http://localhost:3000/api/apple/iap-verify', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: 'Bearer test-token',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
transactionId: '2000000969168810',
|
||||
originalTransactionId: '2000000968585424',
|
||||
}),
|
||||
});
|
||||
|
||||
const response = await POST(request);
|
||||
const data = await response.json();
|
||||
console.log('Response:', data);
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(data.purchase).toBeDefined();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,109 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { vi } from 'vitest';
|
||||
|
||||
export const setupSupabaseMocks = async (
|
||||
customResponses = {
|
||||
getUser: null,
|
||||
select: null,
|
||||
upsert: null,
|
||||
update: null,
|
||||
insert: null,
|
||||
adminUpsert: null,
|
||||
adminSelect: {
|
||||
data: [],
|
||||
error: null,
|
||||
},
|
||||
adminSelectMany: null,
|
||||
adminUpdate: null,
|
||||
adminInsert: null,
|
||||
adminDelete: null,
|
||||
adminSelectSingle: null,
|
||||
},
|
||||
) => {
|
||||
const { supabase, createSupabaseAdminClient } = await import('@/utils/supabase');
|
||||
|
||||
vi.mocked(supabase.auth.getUser).mockResolvedValue(
|
||||
customResponses.getUser || {
|
||||
data: {
|
||||
user: {
|
||||
id: 'test-user-123',
|
||||
email: 'test@example.com',
|
||||
app_metadata: {},
|
||||
user_metadata: {},
|
||||
aud: 'test-aud',
|
||||
created_at: new Date().toISOString(),
|
||||
},
|
||||
},
|
||||
error: null,
|
||||
},
|
||||
);
|
||||
|
||||
vi.mocked(supabase.from).mockReturnValue({
|
||||
select: vi.fn(() => ({
|
||||
eq: vi.fn(() => ({
|
||||
single: vi.fn().mockResolvedValue(customResponses.select || { data: null, error: null }),
|
||||
})),
|
||||
})),
|
||||
upsert: vi.fn().mockResolvedValue(customResponses.upsert || { data: {}, error: null }),
|
||||
update: vi.fn(() => ({
|
||||
eq: vi.fn().mockResolvedValue(customResponses.update || { data: {}, error: null }),
|
||||
})),
|
||||
insert: vi.fn().mockResolvedValue(customResponses.insert || { data: {}, error: null }),
|
||||
} as any);
|
||||
|
||||
vi.mocked(createSupabaseAdminClient).mockReturnValue({
|
||||
from: vi.fn(() => ({
|
||||
select: vi.fn(() => ({
|
||||
eq: vi.fn(() => ({
|
||||
eq: vi.fn(() => ({
|
||||
single: vi.fn().mockResolvedValue({ data: null, error: null }),
|
||||
order: vi.fn().mockResolvedValue({ data: [], error: null }),
|
||||
limit: vi.fn().mockResolvedValue({ data: [], error: null }),
|
||||
})),
|
||||
single: vi
|
||||
.fn()
|
||||
.mockResolvedValue(customResponses.adminSelect || { data: null, error: null }),
|
||||
limit: vi.fn(() => ({
|
||||
single: vi
|
||||
.fn()
|
||||
.mockResolvedValue(customResponses.adminSelect || { data: null, error: null }),
|
||||
})),
|
||||
order: vi
|
||||
.fn()
|
||||
.mockResolvedValue(customResponses.adminSelectMany || { data: [], error: null }),
|
||||
})),
|
||||
neq: vi.fn(() => ({
|
||||
order: vi
|
||||
.fn()
|
||||
.mockResolvedValue(customResponses.adminSelectMany || { data: [], error: null }),
|
||||
})),
|
||||
gt: vi.fn(() => ({
|
||||
order: vi
|
||||
.fn()
|
||||
.mockResolvedValue(customResponses.adminSelectMany || { data: [], error: null }),
|
||||
})),
|
||||
in: vi.fn(() => ({
|
||||
order: vi
|
||||
.fn()
|
||||
.mockResolvedValue(customResponses.adminSelectMany || { data: [], error: null }),
|
||||
})),
|
||||
order: vi
|
||||
.fn()
|
||||
.mockResolvedValue(customResponses.adminSelectMany || { data: [], error: null }),
|
||||
limit: vi
|
||||
.fn()
|
||||
.mockResolvedValue(customResponses.adminSelectMany || { data: [], error: null }),
|
||||
})),
|
||||
upsert: vi.fn().mockResolvedValue(customResponses.adminUpsert || { data: [], error: null }),
|
||||
update: vi.fn(() => ({
|
||||
eq: vi.fn().mockResolvedValue(customResponses.adminUpdate || { data: {}, error: null }),
|
||||
match: vi.fn().mockResolvedValue(customResponses.adminUpdate || { data: {}, error: null }),
|
||||
})),
|
||||
insert: vi.fn().mockResolvedValue(customResponses.adminInsert || { data: {}, error: null }),
|
||||
delete: vi.fn(() => ({
|
||||
eq: vi.fn().mockResolvedValue(customResponses.adminDelete || { data: {}, error: null }),
|
||||
match: vi.fn().mockResolvedValue(customResponses.adminDelete || { data: {}, error: null }),
|
||||
})),
|
||||
})),
|
||||
} as any);
|
||||
};
|
||||
@@ -0,0 +1,71 @@
|
||||
import { describe, it, expect, beforeAll } from 'vitest';
|
||||
import { AppleIAPVerifier, createAppleIAPVerifier } from '@/libs/iap/apple/verifier';
|
||||
|
||||
const SKIP_IAP_API_TESTS = !process.env['ENABLE_IAP_API_TESTS'];
|
||||
const REAL_TEST_DATA = {
|
||||
validSubscription: {
|
||||
transactionId: '2000000969168810',
|
||||
originalTransactionId: '2000000968585424',
|
||||
productId: 'com.bilingify.readest.monthly.plus',
|
||||
},
|
||||
|
||||
expiredSubscription: {
|
||||
transactionId: '2000000969189989',
|
||||
originalTransactionId: '2000000969189989',
|
||||
productId: 'com.bilingify.readest.monthly.plus',
|
||||
},
|
||||
|
||||
refundedTransaction: {
|
||||
transactionId: '1000000555666777',
|
||||
originalTransactionId: '1000000555666777',
|
||||
productId: 'com.bilingify.readest.monthly.plus',
|
||||
},
|
||||
};
|
||||
|
||||
describe.skipIf(SKIP_IAP_API_TESTS)('Apple IAP Integration Tests', () => {
|
||||
let verifier: AppleIAPVerifier;
|
||||
|
||||
beforeAll(() => {
|
||||
verifier = createAppleIAPVerifier({
|
||||
keyId: process.env['APPLE_IAP_KEY_ID']!,
|
||||
issuerId: process.env['APPLE_IAP_ISSUER_ID']!,
|
||||
bundleId: process.env['APPLE_IAP_BUNDLE_ID']!,
|
||||
privateKey: atob(process.env['APPLE_IAP_PRIVATE_KEY_BASE64']!),
|
||||
environment: 'sandbox',
|
||||
});
|
||||
});
|
||||
|
||||
describe('Transaction Verification', () => {
|
||||
it('should verify a real valid transaction', async () => {
|
||||
const { transactionId, originalTransactionId } = REAL_TEST_DATA.validSubscription;
|
||||
|
||||
const result = await verifier.verifyTransaction(originalTransactionId);
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.verified).toBe(true);
|
||||
expect(result.status).toBe('active');
|
||||
expect(result.transactionId).toBe(transactionId);
|
||||
expect(result.originalTransactionId).toBe(originalTransactionId);
|
||||
expect(result.bundleId).toBe(process.env['APPLE_IAP_BUNDLE_ID']);
|
||||
expect(result.productId).toBeTruthy();
|
||||
expect(result.purchaseDate).toBeInstanceOf(Date);
|
||||
}, 10000);
|
||||
|
||||
it('should handle expired subscription correctly', async () => {
|
||||
const { originalTransactionId } = REAL_TEST_DATA.expiredSubscription;
|
||||
|
||||
const result = await verifier.verifyTransaction(originalTransactionId);
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.verified).toBe(true);
|
||||
expect(result.status).toBe('expired');
|
||||
}, 10000);
|
||||
|
||||
it('should reject invalid transaction IDs', async () => {
|
||||
const result = await verifier.verifyTransaction('invalid_transaction_id');
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.error).toBeTruthy();
|
||||
}, 10000);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,273 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
|
||||
import { detectLanguage } from '@/utils/lang';
|
||||
|
||||
describe('detectLanguage - Result Tests', () => {
|
||||
describe('English text detection', () => {
|
||||
it('should detect English text', () => {
|
||||
const englishTexts = [
|
||||
'This is a sample English text for language detection testing.',
|
||||
'The quick brown fox jumps over the lazy dog.',
|
||||
'Hello world! This is an English sentence with punctuation.',
|
||||
'Chapter 1: Introduction to Language Detection',
|
||||
'In the beginning was the Word, and the Word was with God.',
|
||||
'To be or not to be, that is the question.',
|
||||
];
|
||||
|
||||
englishTexts.forEach((text) => {
|
||||
const result = detectLanguage(text);
|
||||
expect(result).toBe('en');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Chinese text detection', () => {
|
||||
it('should detect Chinese text', () => {
|
||||
const chineseTexts = [
|
||||
'这是一个中文文本的示例,用于语言检测测试。',
|
||||
'天下大势,分久必合,合久必分。',
|
||||
'学而时习之,不亦说乎?有朋自远方来,不亦乐乎?',
|
||||
'春眠不觉晓,处处闻啼鸟。夜来风雨声,花落知多少。',
|
||||
'那时候,天空很蓝,云彩很白,我们都还很年轻。',
|
||||
'人工智能技术的发展正在改变我们的生活方式和工作方式。',
|
||||
];
|
||||
|
||||
chineseTexts.forEach((text) => {
|
||||
const result = detectLanguage(text);
|
||||
expect(result).toBe('zh');
|
||||
});
|
||||
});
|
||||
|
||||
it('should detect Traditional Chinese text', () => {
|
||||
const traditionalChineseTexts = [
|
||||
'這是一個正體中文文本的示例,用於語言檢測測試。',
|
||||
'天下大勢,分久必合,合久必分。',
|
||||
'學而時習之,不亦說乎?有朋自遠方來,不亦樂乎?',
|
||||
'春眠不覺曉,處處聞啼鳥。夜來風雨聲,花落知多少。',
|
||||
'那時候,天空很藍,雲彩很白,我們都還很年輕。',
|
||||
'人工智慧技術的發展正在改變我們的生活方式和工作方式。',
|
||||
];
|
||||
|
||||
traditionalChineseTexts.forEach((text) => {
|
||||
const result = detectLanguage(text);
|
||||
expect(result).toBe('zh');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Japanese text detection', () => {
|
||||
it('should detect Japanese text', () => {
|
||||
const japaneseTexts = [
|
||||
'これは日本語のテキストサンプルです。',
|
||||
'第一章:言語検出について',
|
||||
'こんにちは、世界!これは日本語の文章です。',
|
||||
'桜の花が咲く季節になりました。とても美しいです。',
|
||||
'ひらがな、カタカナ、漢字を使って日本語を書きます。',
|
||||
'東京は日本の首都であり、多くの人々が住んでいます。',
|
||||
];
|
||||
|
||||
japaneseTexts.forEach((text) => {
|
||||
const result = detectLanguage(text);
|
||||
expect(result).toBe('ja');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('French text detection', () => {
|
||||
it('should detect French text', () => {
|
||||
const frenchTexts = [
|
||||
'Ceci est un exemple de texte français pour tester la détection de langue.',
|
||||
'Chapitre 1: Introduction à la détection de langue.',
|
||||
'Bonjour le monde! Ceci est une phrase en français.',
|
||||
'Les Champs-Élysées sont une avenue célèbre à Paris, souvent appelée la plus belle avenue du monde.',
|
||||
'Victor Hugo était un écrivain français très célèbre du XIXe siècle,',
|
||||
"La littérature française possède une richesse extraordinaire qui s'étend sur plusieurs siècles.",
|
||||
];
|
||||
|
||||
frenchTexts.forEach((text) => {
|
||||
const result = detectLanguage(text);
|
||||
expect(result).toBe('fr');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Spanish text detection', () => {
|
||||
it('should detect Spanish text', () => {
|
||||
const spanishTexts = [
|
||||
'Este es un ejemplo de texto en español para probar la detección de idioma.',
|
||||
'Capítulo 1: Introducción a la detección de idiomas',
|
||||
'¡Hola mundo! Esta es una oración en español.',
|
||||
'España es un país ubicado en la península ibérica.',
|
||||
'El flamenco es una forma de arte español muy famosa.',
|
||||
'Don Quijote de La Mancha es una obra clásica de la literatura española.',
|
||||
];
|
||||
|
||||
spanishTexts.forEach((text) => {
|
||||
const result = detectLanguage(text);
|
||||
expect(result).toBe('es');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('German text detection', () => {
|
||||
it('should detect German text', () => {
|
||||
const germanTexts = [
|
||||
'Dies ist ein Beispieltext auf Deutsch für die Spracherkennung.',
|
||||
'Kapitel 1: Einführung in die Spracherkennung',
|
||||
'Hallo Welt! Dies ist ein deutscher Satz.',
|
||||
'Deutschland ist ein Land in Mitteleuropa.',
|
||||
'Das Oktoberfest ist ein berühmtes deutsches Festival.',
|
||||
'Die deutsche Sprache hat viele zusammengesetzte Wörter.',
|
||||
];
|
||||
|
||||
germanTexts.forEach((text) => {
|
||||
const result = detectLanguage(text);
|
||||
expect(result).toBe('de');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Arabic text detection', () => {
|
||||
it('should detect Arabic text', () => {
|
||||
const arabicTexts = [
|
||||
'هذا نص عربي للاختبار. يجب أن يتم اكتشاف هذا النص كعربي.',
|
||||
'الفصل الأول: مقدمة في اكتشاف اللغة',
|
||||
'مرحبا بالعالم! هذه جملة باللغة العربية.',
|
||||
'العربية لغة جميلة يتحدث بها ملايين الناس.',
|
||||
'القرآن الكريم نزل باللغة العربية.',
|
||||
'دمشق هي عاصمة سوريا وواحدة من أقدم المدن في العالم.',
|
||||
];
|
||||
|
||||
arabicTexts.forEach((text) => {
|
||||
const result = detectLanguage(text);
|
||||
expect(result).toBe('ar');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Korean text detection', () => {
|
||||
it('should detect Korean text', () => {
|
||||
const koreanTexts = [
|
||||
'이것은 언어 감지 테스트를 위한 한국어 텍스트 샘플입니다.',
|
||||
'제1장: 언어 감지 소개',
|
||||
'안녕하세요 세계! 이것은 한국어 문장입니다.',
|
||||
'서울은 대한민국의 수도이며 많은 사람들이 살고 있습니다.',
|
||||
'한글은 세종대왕이 만든 우리나라의 고유한 문자입니다.',
|
||||
'김치는 한국의 전통 음식 중 하나입니다.',
|
||||
];
|
||||
|
||||
koreanTexts.forEach((text) => {
|
||||
const result = detectLanguage(text);
|
||||
expect(result).toBe('ko');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('fallback behavior', () => {
|
||||
it('should return "en" for very short text', () => {
|
||||
const shortTexts = ['', ' ', 'Hi', 'OK', '123', '!@#'];
|
||||
|
||||
shortTexts.forEach((text) => {
|
||||
const result = detectLanguage(text);
|
||||
expect(result).toBe('en');
|
||||
});
|
||||
});
|
||||
|
||||
it('should return "en" for unrecognizable text', () => {
|
||||
const unrecognizableTexts = [
|
||||
'123456789012345678901234567890',
|
||||
'!@#$%^&*()_+{}|:"<>?[]\\;\',./',
|
||||
'1111111111222222222233333333334444444444',
|
||||
];
|
||||
|
||||
unrecognizableTexts.forEach((text) => {
|
||||
const result = detectLanguage(text);
|
||||
expect(result).toBe('en');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('mixed content', () => {
|
||||
it('should detect predominant language in mixed content', () => {
|
||||
const testCases = [
|
||||
{
|
||||
text: 'This is mostly English text with some 中文 words mixed in but English dominates.',
|
||||
expected: 'en',
|
||||
},
|
||||
{
|
||||
text: '这主要是中文文本,虽然有一些 English words 混合在其中,但中文占主导地位。',
|
||||
expected: 'zh',
|
||||
},
|
||||
{
|
||||
text: 'Ceci est principalement du français avec quelques English words mélangés.',
|
||||
expected: 'fr',
|
||||
},
|
||||
];
|
||||
|
||||
testCases.forEach(({ text, expected }) => {
|
||||
const result = detectLanguage(text);
|
||||
expect(result).toBe(expected);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('long text handling', () => {
|
||||
it('should handle long text (over 1000 characters)', () => {
|
||||
const longEnglishText = 'This is a very long English text. '.repeat(100); // ~3400 chars
|
||||
const longChineseText = '这是一个很长的中文文本。'.repeat(100); // ~1200 chars
|
||||
const longFrenchText = 'Ceci est un très long texte français. '.repeat(100); // ~3800 chars
|
||||
|
||||
expect(detectLanguage(longEnglishText)).toBe('en');
|
||||
expect(detectLanguage(longChineseText)).toBe('zh');
|
||||
expect(detectLanguage(longFrenchText)).toBe('fr');
|
||||
});
|
||||
});
|
||||
|
||||
describe('edge cases with whitespace and formatting', () => {
|
||||
it('should handle text with lots of whitespace', () => {
|
||||
const textWithWhitespace = ' This is English text with lots of spaces ';
|
||||
const result = detectLanguage(textWithWhitespace);
|
||||
expect(result).toBe('en');
|
||||
});
|
||||
|
||||
it('should handle text with newlines and tabs', () => {
|
||||
const textWithFormatting = 'This is English text\nwith newlines\tand tabs\nfor formatting.';
|
||||
const result = detectLanguage(textWithFormatting);
|
||||
expect(result).toBe('en');
|
||||
});
|
||||
|
||||
it('should handle text with punctuation', () => {
|
||||
const textWithPunctuation = 'Hello, world! How are you? I am fine. Thank you very much!!!';
|
||||
const result = detectLanguage(textWithPunctuation);
|
||||
expect(result).toBe('en');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('detectLanguage - Real World Samples', () => {
|
||||
it('should detect language in book excerpts', () => {
|
||||
const bookExcerpts = [
|
||||
{
|
||||
text: 'It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness.',
|
||||
expected: 'en',
|
||||
},
|
||||
{
|
||||
text: 'Dans une trou dans la terre vivait un hobbit. Pas un trou déplaisant, sale et humide.',
|
||||
expected: 'fr',
|
||||
},
|
||||
{
|
||||
text: '昔々、あるところにおじいさんとおばあさんが住んでいました。',
|
||||
expected: 'ja',
|
||||
},
|
||||
{
|
||||
text: '很久很久以前,有一个美丽的公主住在一座城堡里。',
|
||||
expected: 'zh',
|
||||
},
|
||||
];
|
||||
|
||||
bookExcerpts.forEach(({ text, expected }) => {
|
||||
const result = detectLanguage(text);
|
||||
expect(result).toBe(expected);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,385 @@
|
||||
import { describe, it, expect, beforeEach } from 'vitest';
|
||||
import { XCFI } from '@/utils/xcfi';
|
||||
|
||||
describe('CFIToXPointerConverter', () => {
|
||||
let converter: XCFI;
|
||||
let simpleDoc: Document;
|
||||
let complexDoc: Document;
|
||||
|
||||
beforeEach(() => {
|
||||
simpleDoc = new DOMParser().parseFromString(
|
||||
`
|
||||
<html>
|
||||
<head>
|
||||
<title>Simple Document</title>
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<p>First paragraph</p>
|
||||
<p>Second paragraph with some text</p>
|
||||
<p>Third paragraph</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
`,
|
||||
'text/html',
|
||||
);
|
||||
|
||||
complexDoc = new DOMParser().parseFromString(
|
||||
`
|
||||
<html>
|
||||
<head>
|
||||
<title>Complex Document</title>
|
||||
</head>
|
||||
<body>
|
||||
<section>
|
||||
<h1>Chapter 1</h1>
|
||||
<p>First paragraph</p>
|
||||
<p>Second paragraph</p>
|
||||
</section>
|
||||
<section>
|
||||
<h1>Chapter 2</h1>
|
||||
<p id="special">Another paragraph</p>
|
||||
<p>Final paragraph with <em>emphasis</em> and more text</p>
|
||||
</section>
|
||||
</body>
|
||||
</html>
|
||||
`,
|
||||
'text/html',
|
||||
);
|
||||
});
|
||||
|
||||
describe('static methods', () => {
|
||||
it('should extract spine index from CFI', () => {
|
||||
const cfi1 = 'epubcfi(/6/2!/4/2/4)'; // Spine index 0
|
||||
const cfi2 = 'epubcfi(/6/4!/4/2/4)'; // Spine index 1
|
||||
const cfi3 = 'epubcfi(/6/10!/4/2/4)'; // Spine index 4
|
||||
|
||||
expect(XCFI.extractSpineIndex(cfi1)).toBe(0);
|
||||
expect(XCFI.extractSpineIndex(cfi2)).toBe(1);
|
||||
expect(XCFI.extractSpineIndex(cfi3)).toBe(4);
|
||||
});
|
||||
|
||||
it('should extract spine index from range CFI', () => {
|
||||
const rangeCfi = 'epubcfi(/6/8!/4/2/2/1:5,/6/8!/4/2/4/1:10)'; // Spine index 3
|
||||
expect(XCFI.extractSpineIndex(rangeCfi)).toBe(3);
|
||||
});
|
||||
|
||||
it('should extract spine index from CFI with assertions', () => {
|
||||
const cfi = 'epubcfi(/6/1266!/4,/76,/88/1:85)'; // Complex CFI, spine index 632
|
||||
expect(XCFI.extractSpineIndex(cfi)).toBe(632);
|
||||
});
|
||||
|
||||
it('should throw error for invalid CFI in extractSpineIndex', () => {
|
||||
const invalidCfi = 'invalid-cfi';
|
||||
expect(() => XCFI.extractSpineIndex(invalidCfi)).toThrow('Cannot extract spine index');
|
||||
});
|
||||
});
|
||||
|
||||
describe('round-trip conversion - point CFI', () => {
|
||||
beforeEach(() => {
|
||||
converter = new XCFI(simpleDoc, 1);
|
||||
});
|
||||
|
||||
it('should convert first element CFI round-trip', () => {
|
||||
const originalCfi = 'epubcfi(/6/4!/4/2/2)'; // First p element
|
||||
const xpointer = converter.cfiToXPointer(originalCfi);
|
||||
const convertedCfi = converter.xPointerToCFI(xpointer.xpointer);
|
||||
|
||||
expect(originalCfi).toEqual(convertedCfi);
|
||||
expect(xpointer).toEqual({
|
||||
xpointer: '/body/DocFragment[2]/body/div/p[0]',
|
||||
});
|
||||
});
|
||||
|
||||
it('should convert basic element CFI round-trip', () => {
|
||||
const originalCfi = 'epubcfi(/6/4!/4/2/4)'; // Second p element
|
||||
const xpointer = converter.cfiToXPointer(originalCfi);
|
||||
const convertedCfi = converter.xPointerToCFI(xpointer.xpointer);
|
||||
|
||||
expect(originalCfi).toEqual(convertedCfi);
|
||||
expect(xpointer).toEqual({
|
||||
xpointer: '/body/DocFragment[2]/body/div/p[1]',
|
||||
});
|
||||
});
|
||||
|
||||
it('should convert third element CFI round-trip', () => {
|
||||
const originalCfi = 'epubcfi(/6/4!/4/2/6)'; // Third p element
|
||||
const xpointer = converter.cfiToXPointer(originalCfi);
|
||||
const convertedCfi = converter.xPointerToCFI(xpointer.xpointer);
|
||||
|
||||
expect(originalCfi).toEqual(convertedCfi);
|
||||
expect(xpointer).toEqual({
|
||||
xpointer: '/body/DocFragment[2]/body/div/p[2]',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('round-trip conversion - range CFI', () => {
|
||||
beforeEach(() => {
|
||||
converter = new XCFI(simpleDoc, 2);
|
||||
});
|
||||
|
||||
it('should convert standard range CFI', () => {
|
||||
const originalCfi = 'epubcfi(/6/6!/4/2,/2/1:6,/4/1:16)'; // From first p:6 to second p:16
|
||||
const xpointer = converter.cfiToXPointer(originalCfi);
|
||||
const convertedCfi = converter.xPointerToCFI(xpointer.pos0!, xpointer.pos1!);
|
||||
|
||||
expect(originalCfi).toEqual(convertedCfi);
|
||||
expect(xpointer.xpointer).toEqual('/body/DocFragment[3]/body/div/p[0]/text().6');
|
||||
expect(xpointer.pos0).toEqual('/body/DocFragment[3]/body/div/p[0]/text().6');
|
||||
expect(xpointer.pos1).toEqual('/body/DocFragment[3]/body/div/p[1]/text().16');
|
||||
});
|
||||
|
||||
it('should convert range CFI within same element', () => {
|
||||
const originalCfi = 'epubcfi(/6/6!/4/2/4,/1:5,/1:10)'; // Within second p element
|
||||
const xpointer = converter.cfiToXPointer(originalCfi);
|
||||
const convertedCfi = converter.xPointerToCFI(xpointer.pos0!, xpointer.pos1!);
|
||||
|
||||
expect(originalCfi).toEqual(convertedCfi);
|
||||
expect(xpointer.pos0).toMatch(/\/text\(\)\.5$/);
|
||||
expect(xpointer.pos1).toMatch(/\/text\(\)\.10$/);
|
||||
});
|
||||
|
||||
it('should handle range across multiple elements', () => {
|
||||
const originalCfi = 'epubcfi(/6/6!/4/2,/2,/6)'; // From first to third p
|
||||
const xpointer = converter.cfiToXPointer(originalCfi);
|
||||
const convertedCfi = converter.xPointerToCFI(xpointer.pos0!, xpointer.pos1!);
|
||||
|
||||
expect(originalCfi).toEqual(convertedCfi);
|
||||
expect(xpointer.pos0).toMatch(/p\[0\]/);
|
||||
expect(xpointer.pos1).toMatch(/p\[2\]/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('round-trip conversion - complex document', () => {
|
||||
beforeEach(() => {
|
||||
converter = new XCFI(complexDoc, 3);
|
||||
});
|
||||
|
||||
it('should handle nested elements', () => {
|
||||
const originalCfi = 'epubcfi(/6/8!/4/2/2)'; // First section
|
||||
const xpointer = converter.cfiToXPointer(originalCfi);
|
||||
const convertedCfi = converter.xPointerToCFI(xpointer.xpointer);
|
||||
|
||||
expect(originalCfi).toEqual(convertedCfi);
|
||||
expect(xpointer.xpointer).toMatch(/\/body\/section\[0\]/);
|
||||
});
|
||||
|
||||
it('should handle elements with IDs', () => {
|
||||
const originalCfi = 'epubcfi(/6/8!/4/4/4[special])'; // Element with id="special"
|
||||
const xpointer = converter.cfiToXPointer(originalCfi);
|
||||
const convertedCfi = converter.xPointerToCFI(xpointer.xpointer);
|
||||
|
||||
expect(originalCfi).toEqual(convertedCfi);
|
||||
expect(xpointer.xpointer).toMatch(/\/body\/section\[1\]\/p\[0\]/);
|
||||
});
|
||||
|
||||
it('should handle inline elements', () => {
|
||||
const originalCfi = 'epubcfi(/6/8!/4/4/6)'; // Text with inline em element
|
||||
const xpointer = converter.cfiToXPointer(originalCfi);
|
||||
const convertedCfi = converter.xPointerToCFI(xpointer.xpointer);
|
||||
|
||||
expect(originalCfi).toEqual(convertedCfi);
|
||||
expect(xpointer.xpointer).toMatch(/\/body\/section\[1\]\/p\[1\]/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('convertCFI - error handling', () => {
|
||||
beforeEach(() => {
|
||||
converter = new XCFI(simpleDoc, 0);
|
||||
});
|
||||
|
||||
it('should throw error for invalid CFI format', () => {
|
||||
const invalidCfi = 'invalid-cfi';
|
||||
expect(() => converter.cfiToXPointer(invalidCfi)).toThrow('Failed to convert CFI');
|
||||
});
|
||||
|
||||
it('should throw error for CFI with invalid path', () => {
|
||||
const invalidCfi = 'epubcfi(/6/999!/2/2)'; // Non-existent path
|
||||
expect(() => converter.cfiToXPointer(invalidCfi)).toThrow();
|
||||
});
|
||||
|
||||
it('should handle malformed CFI gracefully', () => {
|
||||
const malformedCfi = 'epubcfi(/6/2/2';
|
||||
expect(() => converter.cfiToXPointer(malformedCfi)).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('xPointerToCFI - direct XPointer input', () => {
|
||||
beforeEach(() => {
|
||||
converter = new XCFI(simpleDoc, 1);
|
||||
});
|
||||
|
||||
it('should convert XPointer to CFI for first element', () => {
|
||||
const xpointer = '/body/DocFragment[2]/body/div/p[0]';
|
||||
const cfi = converter.xPointerToCFI(xpointer);
|
||||
|
||||
// Verify by converting back to XPointer
|
||||
const backToXPointer = converter.cfiToXPointer(cfi);
|
||||
expect(backToXPointer.xpointer).toBe(xpointer);
|
||||
});
|
||||
|
||||
it('should convert XPointer to CFI for second element', () => {
|
||||
const xpointer = '/body/DocFragment[2]/body/div/p[1]';
|
||||
const cfi = converter.xPointerToCFI(xpointer);
|
||||
|
||||
const backToXPointer = converter.cfiToXPointer(cfi);
|
||||
expect(backToXPointer.xpointer).toBe(xpointer);
|
||||
});
|
||||
|
||||
it('should convert XPointer with text offset to CFI', () => {
|
||||
const xpointer = '/body/DocFragment[2]/body/div[0]/p[0]/text().6';
|
||||
const cfi = converter.xPointerToCFI(xpointer);
|
||||
expect(cfi).toBe('epubcfi(/6/4!/4/2/2/1:6)');
|
||||
});
|
||||
|
||||
it('should convert range XPointer to CFI', () => {
|
||||
const pos0 = '/body/DocFragment[2]/body/div/p[0]/text().6';
|
||||
const pos1 = '/body/DocFragment[2]/body/div/p[1]/text().16';
|
||||
const cfi = converter.xPointerToCFI(pos0, pos1);
|
||||
const xpointer = converter.cfiToXPointer(cfi);
|
||||
|
||||
expect(cfi).toMatch(/^epubcfi\([^,]+,[^,]+,[^,]+\)$/);
|
||||
expect(xpointer.pos0).toBe(pos0);
|
||||
expect(xpointer.pos1).toBe(pos1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('xPointerToCFI - error handling', () => {
|
||||
beforeEach(() => {
|
||||
converter = new XCFI(simpleDoc, 0);
|
||||
});
|
||||
|
||||
it('should throw error for invalid XPointer format', () => {
|
||||
const invalidXPointer = 'invalid-xpointer';
|
||||
expect(() => converter.xPointerToCFI(invalidXPointer)).toThrow('Failed to convert XPointer');
|
||||
});
|
||||
|
||||
it('should throw error for XPointer with non-existent path', () => {
|
||||
const invalidXPointer = '/body/DocFragment[1]/body/nonexistent[999]';
|
||||
expect(() => converter.xPointerToCFI(invalidXPointer)).toThrow();
|
||||
});
|
||||
|
||||
it('should throw error for malformed XPointer', () => {
|
||||
const malformedXPointer = '/body/DocFragment[1]/body/div[';
|
||||
expect(() => converter.xPointerToCFI(malformedXPointer)).toThrow();
|
||||
});
|
||||
|
||||
it('should handle CFI without spine step prefix', () => {
|
||||
// Test the adjustSpineIndex method handles CFIs that don't start with /6/n!
|
||||
const converter = new XCFI(simpleDoc, 3); // Use different spine index
|
||||
const xpointer = '/body/DocFragment[4]/body/div/p[0]';
|
||||
const cfi = converter.xPointerToCFI(xpointer);
|
||||
|
||||
// Verify the spine step is correctly added/adjusted
|
||||
expect(cfi).toMatch(/^epubcfi\(\/6\/8!/); // (3+1)*2 = 8
|
||||
|
||||
// Verify round-trip works
|
||||
const backToXPointer = converter.cfiToXPointer(cfi);
|
||||
expect(backToXPointer.xpointer).toBe(xpointer);
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateCFI', () => {
|
||||
beforeEach(() => {
|
||||
converter = new XCFI(simpleDoc, 0);
|
||||
});
|
||||
|
||||
it('should validate correct CFI', () => {
|
||||
const validCfi = 'epubcfi(/6/2!/4/4)';
|
||||
expect(converter.validateCFI(validCfi)).toBe(true);
|
||||
});
|
||||
|
||||
it('should invalidate incorrect CFI format', () => {
|
||||
const invalidCfi = 'invalid-cfi';
|
||||
expect(converter.validateCFI(invalidCfi)).toBe(false);
|
||||
});
|
||||
|
||||
it('should invalidate CFI with wrong path', () => {
|
||||
const invalidCfi = 'epubcfi(/6/2!/998/2/2)';
|
||||
expect(converter.validateCFI(invalidCfi)).toBe(false);
|
||||
});
|
||||
|
||||
it('should validate range CFI', () => {
|
||||
const validRangeCfi = 'epubcfi(/6/2!/4/2,/2/1:5,/4/1:10)';
|
||||
expect(converter.validateCFI(validRangeCfi)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('edge cases', () => {
|
||||
beforeEach(() => {
|
||||
converter = new XCFI(simpleDoc, 0);
|
||||
});
|
||||
|
||||
it('should handle empty elements', () => {
|
||||
const emptyDoc = new DOMParser().parseFromString(
|
||||
`
|
||||
<html>
|
||||
<body>
|
||||
<div>
|
||||
<p></p>
|
||||
<p>Non-empty</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
`,
|
||||
'text/html',
|
||||
);
|
||||
|
||||
const converter = new XCFI(emptyDoc, 2);
|
||||
const cfi = 'epubcfi(/6/6!/4/2/2)'; // Empty p element
|
||||
const result = converter.cfiToXPointer(cfi);
|
||||
|
||||
expect(result.xpointer).toBe('/body/DocFragment[3]/body/div/p[0]');
|
||||
});
|
||||
|
||||
it('should handle whitespace-only text nodes', () => {
|
||||
const whitespaceDoc = new DOMParser().parseFromString(
|
||||
`
|
||||
<html>
|
||||
<body>
|
||||
<div>
|
||||
<p> </p>
|
||||
<p>Real content</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
`,
|
||||
'text/html',
|
||||
);
|
||||
|
||||
const converter = new XCFI(whitespaceDoc, 2);
|
||||
const cfi = 'epubcfi(/6/6!/4/2/4)'; // Second p element
|
||||
const result = converter.cfiToXPointer(cfi);
|
||||
|
||||
expect(result.xpointer).toBe('/body/DocFragment[3]/body/div/p[1]');
|
||||
});
|
||||
|
||||
it('should handle deeply nested elements', () => {
|
||||
const nestedDoc = new DOMParser().parseFromString(
|
||||
`
|
||||
<html>
|
||||
<body>
|
||||
<div>
|
||||
<section>
|
||||
<article>
|
||||
<p>Deeply nested p0</p>
|
||||
<p>Deeply nested p1</p>
|
||||
</article>
|
||||
</section>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
`,
|
||||
'text/html',
|
||||
);
|
||||
|
||||
const converter = new XCFI(nestedDoc, 2);
|
||||
const cfi = 'epubcfi(/6/6!/4/2/2/2/2)'; // Deeply nested p
|
||||
const result = converter.cfiToXPointer(cfi);
|
||||
|
||||
expect(result.xpointer).toBe('/body/DocFragment[3]/body/div/section/article/p[0]');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,241 @@
|
||||
import { z } from 'zod';
|
||||
import { NextResponse } from 'next/server';
|
||||
import { getAppleIAPVerifier } from '@/libs/iap/apple/verifier';
|
||||
import { createSupabaseAdminClient } from '@/utils/supabase';
|
||||
import { validateUserAndToken } from '@/utils/access';
|
||||
import { IAPError } from '@/types/error';
|
||||
|
||||
const iapVerificationSchema = z.object({
|
||||
transactionId: z.string().min(1, 'Transaction ID is required'),
|
||||
originalTransactionId: z.string().min(1, 'Original Transaction ID is required'),
|
||||
});
|
||||
|
||||
const PRODUCT_MAP: Record<string, string> = {
|
||||
'com.bilingify.readest.monthly.plus': 'Plus',
|
||||
'com.bilingify.readest.yearly.plus': 'Plus',
|
||||
'com.bilingify.readest.monthly.pro': 'Pro',
|
||||
'com.bilingify.readest.yearly.pro': 'Pro',
|
||||
};
|
||||
|
||||
const getProductName = (productId: string) => {
|
||||
return PRODUCT_MAP[productId] || productId;
|
||||
};
|
||||
|
||||
const getProductPlan = (productId: string) => {
|
||||
if (productId.includes('plus')) {
|
||||
return 'plus';
|
||||
} else if (productId.includes('pro')) {
|
||||
return 'pro';
|
||||
}
|
||||
return 'free';
|
||||
};
|
||||
|
||||
interface Purchase {
|
||||
status: string;
|
||||
customerEmail: string;
|
||||
subscriptionId: string;
|
||||
planName: string;
|
||||
productId: string;
|
||||
platform: string;
|
||||
transactionId: string;
|
||||
originalTransactionId: string;
|
||||
purchaseDate?: string;
|
||||
expiresDate?: string | null;
|
||||
quantity: number;
|
||||
environment: string;
|
||||
bundleId: string;
|
||||
webOrderLineItemId?: string;
|
||||
subscriptionGroupIdentifier?: string;
|
||||
type?: string;
|
||||
revocationDate?: string | null;
|
||||
revocationReason?: number | null;
|
||||
}
|
||||
|
||||
async function updateUserSubscription(userId: string, purchase: Purchase) {
|
||||
try {
|
||||
const supabase = createSupabaseAdminClient();
|
||||
const { data, error } = await supabase.from('apple_iap_subscriptions').upsert(
|
||||
{
|
||||
user_id: userId,
|
||||
platform: purchase.platform,
|
||||
product_id: purchase.productId,
|
||||
transaction_id: purchase.transactionId,
|
||||
original_transaction_id: purchase.originalTransactionId,
|
||||
status: purchase.status === 'active' ? 'active' : 'expired',
|
||||
purchase_date: purchase.purchaseDate,
|
||||
expires_date: purchase.expiresDate,
|
||||
environment: purchase.environment,
|
||||
bundle_id: purchase.bundleId,
|
||||
quantity: purchase.quantity || 1,
|
||||
auto_renew_status: true,
|
||||
web_order_line_item_id: purchase.webOrderLineItemId,
|
||||
subscription_group_identifier: purchase.subscriptionGroupIdentifier,
|
||||
created_at: new Date(),
|
||||
updated_at: new Date(),
|
||||
},
|
||||
{
|
||||
onConflict: 'user_id,original_transaction_id',
|
||||
},
|
||||
);
|
||||
|
||||
if (error) {
|
||||
console.error('Database update error:', error);
|
||||
throw new Error(`Database update failed: ${error.message}`);
|
||||
}
|
||||
|
||||
const plan = await getProductPlan(purchase.productId);
|
||||
await supabase
|
||||
.from('plans')
|
||||
.update({
|
||||
plan: ['active', 'trialing'].includes(purchase.status) ? plan : 'free',
|
||||
status: purchase.status,
|
||||
})
|
||||
.eq('id', userId);
|
||||
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error('Failed to update user subscription:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function POST(request: Request) {
|
||||
const body = await request.json();
|
||||
let validatedInput;
|
||||
try {
|
||||
validatedInput = iapVerificationSchema.parse(body);
|
||||
} catch (error) {
|
||||
if (error instanceof z.ZodError) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: 'Invalid input data',
|
||||
purchase: null,
|
||||
},
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
}
|
||||
const { transactionId, originalTransactionId } = validatedInput!;
|
||||
|
||||
const { user, token } = await validateUserAndToken(request.headers.get('authorization'));
|
||||
if (!user || !token) {
|
||||
return NextResponse.json({ error: IAPError.NOT_AUTHENTICATED }, { status: 403 });
|
||||
}
|
||||
|
||||
try {
|
||||
const supabase = createSupabaseAdminClient();
|
||||
const { data: existingSubscription } = await supabase
|
||||
.from('apple_iap_subscriptions')
|
||||
.select('*')
|
||||
.eq('original_transaction_id', originalTransactionId)
|
||||
.single();
|
||||
|
||||
console.log('Existing subscription:', existingSubscription);
|
||||
// Should not restore purchase for another account
|
||||
if (existingSubscription && existingSubscription.user_id !== user.id) {
|
||||
return NextResponse.json(
|
||||
{ error: IAPError.TRANSACTION_BELONGS_TO_ANOTHER_USER },
|
||||
{ status: 403 },
|
||||
);
|
||||
}
|
||||
if (
|
||||
existingSubscription &&
|
||||
existingSubscription.transactionId === transactionId &&
|
||||
existingSubscription.status === 'active'
|
||||
) {
|
||||
console.log('Transaction already verified and active');
|
||||
|
||||
const purchase = {
|
||||
status: existingSubscription.status,
|
||||
customerEmail: user.email,
|
||||
subscriptionId:
|
||||
existingSubscription.web_order_line_item_id ||
|
||||
existingSubscription.original_transaction_id,
|
||||
planName: getProductName(existingSubscription.product_id),
|
||||
productId: existingSubscription.product_id,
|
||||
platform: existingSubscription.platform,
|
||||
transactionId: existingSubscription.transaction_id,
|
||||
originalTransactionId: existingSubscription.original_transaction_id,
|
||||
purchaseDate: existingSubscription.purchase_date,
|
||||
expiresDate: existingSubscription.expires_date,
|
||||
quantity: existingSubscription.quantity,
|
||||
environment: existingSubscription.environment,
|
||||
bundleId: existingSubscription.bundle_id,
|
||||
};
|
||||
|
||||
return NextResponse.json({
|
||||
purchase,
|
||||
error: null,
|
||||
});
|
||||
}
|
||||
|
||||
const defaultIAPVerifier = getAppleIAPVerifier();
|
||||
const verificationResult = await defaultIAPVerifier.verifyTransaction(originalTransactionId);
|
||||
if (!verificationResult.success) {
|
||||
console.error('Apple verification failed:', verificationResult.error);
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: verificationResult.error || IAPError.UNKNOWN_ERROR,
|
||||
purchase: null,
|
||||
},
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
const transaction = verificationResult.transaction!;
|
||||
console.log('Apple verification successful:', {
|
||||
transactionId: transaction.transactionId,
|
||||
productId: transaction.productId,
|
||||
environment: transaction.environment,
|
||||
});
|
||||
if (transaction.environment === 'Sandbox' && process.env.NODE_ENV === 'production') {
|
||||
console.warn('Sandbox transaction in production environment');
|
||||
}
|
||||
|
||||
const purchase = {
|
||||
status: verificationResult.status!,
|
||||
customerEmail: user.email!,
|
||||
subscriptionId: transaction.webOrderLineItemId || transaction.originalTransactionId,
|
||||
planName: getProductName(transaction.productId),
|
||||
productId: transaction.productId,
|
||||
platform: 'ios',
|
||||
transactionId: transaction.transactionId,
|
||||
originalTransactionId: transaction.originalTransactionId,
|
||||
purchaseDate: verificationResult.purchaseDate?.toISOString(),
|
||||
expiresDate: verificationResult.expiresDate?.toISOString() || null,
|
||||
quantity: transaction.quantity,
|
||||
environment: transaction.environment.toLowerCase(),
|
||||
bundleId: transaction.bundleId,
|
||||
webOrderLineItemId: transaction.webOrderLineItemId,
|
||||
subscriptionGroupIdentifier: transaction.subscriptionGroupIdentifier,
|
||||
type: transaction.type,
|
||||
revocationDate: verificationResult.revocationDate?.toISOString() || null,
|
||||
revocationReason: verificationResult.revocationReason,
|
||||
};
|
||||
|
||||
try {
|
||||
await updateUserSubscription(user.id, purchase);
|
||||
} catch (dbError) {
|
||||
console.error('Database update failed:', dbError);
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: IAPError.TRANSACTION_SERVICE_UNAVAILABLE,
|
||||
purchase: null,
|
||||
},
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
purchase,
|
||||
error: null,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('IAP verification error:', error);
|
||||
return NextResponse.json(
|
||||
{ error: error instanceof Error ? error.message : IAPError.UNKNOWN_ERROR },
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { MetadataService } from '@/services/metadata/service';
|
||||
import { SearchRequest } from '@/services/metadata/types';
|
||||
import { validateUserAndToken } from '@/utils/access';
|
||||
|
||||
interface ApiResponse<T> {
|
||||
success: boolean;
|
||||
data?: T;
|
||||
error?: string;
|
||||
timestamp: string;
|
||||
responseTime?: number;
|
||||
}
|
||||
|
||||
function validateSearchRequest(body: SearchRequest): {
|
||||
isValid: boolean;
|
||||
error?: string;
|
||||
data?: SearchRequest;
|
||||
} {
|
||||
const { title, isbn, author, language } = body;
|
||||
|
||||
if (
|
||||
(!title || typeof title !== 'string' || title.trim().length === 0) &&
|
||||
(!isbn || typeof isbn !== 'string' || isbn.trim().length === 0)
|
||||
) {
|
||||
return {
|
||||
isValid: false,
|
||||
error: 'Either title or isbn parameter is required and must be a non-empty string',
|
||||
};
|
||||
}
|
||||
|
||||
if (title && typeof title !== 'string') {
|
||||
return { isValid: false, error: 'Title must be a string if provided' };
|
||||
}
|
||||
|
||||
if (isbn && typeof isbn !== 'string') {
|
||||
return { isValid: false, error: 'ISBN must be a string if provided' };
|
||||
}
|
||||
|
||||
if (author && typeof author !== 'string') {
|
||||
return { isValid: false, error: 'Author must be a string if provided' };
|
||||
}
|
||||
|
||||
if (isbn) {
|
||||
const cleanIsbn = isbn.replace(/[-\s]/g, '');
|
||||
if (!/^\d{10}(\d{3})?$/.test(cleanIsbn)) {
|
||||
return { isValid: false, error: 'Invalid ISBN format. Must be 10 or 13 digits' };
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
isValid: true,
|
||||
data: {
|
||||
title: title?.trim(),
|
||||
isbn: isbn?.trim(),
|
||||
author: author?.trim(),
|
||||
language: language?.trim(),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function createResponse<T>(
|
||||
success: boolean,
|
||||
data: T | null = null,
|
||||
error: string | null = null,
|
||||
responseTime: number,
|
||||
): ApiResponse<T> {
|
||||
return {
|
||||
success,
|
||||
data: data || undefined,
|
||||
error: error || undefined,
|
||||
timestamp: new Date().toISOString(),
|
||||
responseTime,
|
||||
};
|
||||
}
|
||||
|
||||
let metadataService: MetadataService;
|
||||
|
||||
function getMetadataService(): MetadataService {
|
||||
if (!metadataService) {
|
||||
metadataService = new MetadataService({
|
||||
googleBooksApiKeys: process.env['GOOGLE_BOOKS_API_KEYS'],
|
||||
});
|
||||
}
|
||||
return metadataService;
|
||||
}
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
const { user, token } = await validateUserAndToken(request.headers.get('authorization'));
|
||||
if (!user || !token) {
|
||||
return NextResponse.json({ error: 'Not authenticated' }, { status: 403 });
|
||||
}
|
||||
|
||||
const startTime = Date.now();
|
||||
|
||||
try {
|
||||
const body = await request.json();
|
||||
const validation = validateSearchRequest(body);
|
||||
if (!validation.isValid) {
|
||||
const responseTime = Date.now() - startTime;
|
||||
return NextResponse.json(createResponse(false, null, validation.error!, responseTime), {
|
||||
status: 400,
|
||||
});
|
||||
}
|
||||
|
||||
const service = getMetadataService();
|
||||
const result = await service.search(validation.data!);
|
||||
const responseTime = Date.now() - startTime;
|
||||
|
||||
if (!result) {
|
||||
return NextResponse.json(createResponse(false, null, 'Book not found', responseTime), {
|
||||
status: 404,
|
||||
});
|
||||
}
|
||||
|
||||
return NextResponse.json(createResponse(true, result, null, responseTime), {
|
||||
status: 200,
|
||||
});
|
||||
} catch (error) {
|
||||
const responseTime = Date.now() - startTime;
|
||||
console.error('Search API error:', error);
|
||||
|
||||
let errorMessage = 'Internal server error';
|
||||
let statusCode = 500;
|
||||
|
||||
if (error instanceof Error) {
|
||||
errorMessage = error.message;
|
||||
|
||||
if (error.message.includes('rate limit')) {
|
||||
statusCode = 429;
|
||||
} else if (error.message.includes('forbidden') || error.message.includes('API key')) {
|
||||
statusCode = 403;
|
||||
} else if (error.message.includes('Invalid ISBN')) {
|
||||
statusCode = 400;
|
||||
}
|
||||
}
|
||||
|
||||
return NextResponse.json(createResponse(false, null, errorMessage, responseTime), {
|
||||
status: statusCode,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import Stripe from 'stripe';
|
||||
import { NextResponse } from 'next/server';
|
||||
import { getStripe } from '@/libs/stripe/server';
|
||||
import { createOrUpdateSubscription } from '@/utils/stripe';
|
||||
import { validateUserAndToken } from '@/utils/access';
|
||||
|
||||
export async function POST(request: Request) {
|
||||
const { sessionId } = await request.json();
|
||||
|
||||
const { user, token } = await validateUserAndToken(request.headers.get('authorization'));
|
||||
if (!user || !token) {
|
||||
return NextResponse.json({ error: 'Not authenticated' }, { status: 403 });
|
||||
}
|
||||
|
||||
try {
|
||||
const stripe = getStripe();
|
||||
const session = await stripe.checkout.sessions.retrieve(sessionId);
|
||||
|
||||
if (session.payment_status === 'paid') {
|
||||
const customerId = session.customer as string;
|
||||
const subscriptionId = session.subscription as string;
|
||||
await createOrUpdateSubscription(user.id, customerId, subscriptionId);
|
||||
}
|
||||
|
||||
return NextResponse.json({ session });
|
||||
} catch (error) {
|
||||
if (error instanceof Stripe.errors.StripeError) {
|
||||
console.error('Stripe error:', error);
|
||||
return NextResponse.json({ error: error.message }, { status: 500 });
|
||||
}
|
||||
return NextResponse.json(
|
||||
{ error: error instanceof Error ? error.message : 'Unknown error' },
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { getStripe } from '@/libs/stripe/server';
|
||||
import { validateUserAndToken } from '@/utils/access';
|
||||
import { createSupabaseAdminClient } from '@/utils/supabase';
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
const { priceId, embedded = true, metadata = {} } = await request.json();
|
||||
|
||||
const { user, token } = await validateUserAndToken(request.headers.get('authorization'));
|
||||
if (!user || !token) {
|
||||
return NextResponse.json({ error: 'Not authenticated' }, { status: 403 });
|
||||
}
|
||||
|
||||
const enhancedMetadata = {
|
||||
...metadata,
|
||||
userId: user.id,
|
||||
};
|
||||
|
||||
try {
|
||||
const supabase = createSupabaseAdminClient();
|
||||
const { data: customerData } = await supabase
|
||||
.from('customers')
|
||||
.select('stripe_customer_id')
|
||||
.eq('user_id', user.id)
|
||||
.single();
|
||||
|
||||
let customerId;
|
||||
if (!customerData?.stripe_customer_id) {
|
||||
const stripe = getStripe();
|
||||
const customer = await stripe.customers.create({
|
||||
email: user.email,
|
||||
metadata: {
|
||||
userId: user.id,
|
||||
},
|
||||
});
|
||||
customerId = customer.id;
|
||||
await supabase.from('customers').insert({
|
||||
user_id: user.id,
|
||||
stripe_customer_id: customerId,
|
||||
});
|
||||
} else {
|
||||
customerId = customerData.stripe_customer_id;
|
||||
}
|
||||
|
||||
const stripe = getStripe();
|
||||
const successUrl = `${request.headers.get('origin')}/user/subscription/success?session_id={CHECKOUT_SESSION_ID}`;
|
||||
const returnUrl = `${request.headers.get('origin')}/user`;
|
||||
const session = await stripe.checkout.sessions.create({
|
||||
ui_mode: embedded ? 'embedded' : 'hosted',
|
||||
customer: customerId,
|
||||
mode: 'subscription',
|
||||
payment_method_types: ['card'],
|
||||
line_items: [
|
||||
{
|
||||
price: priceId,
|
||||
quantity: 1,
|
||||
},
|
||||
],
|
||||
metadata: enhancedMetadata,
|
||||
success_url: embedded ? undefined : successUrl,
|
||||
cancel_url: embedded ? undefined : returnUrl,
|
||||
redirect_on_completion: embedded ? 'never' : undefined,
|
||||
});
|
||||
|
||||
return NextResponse.json({
|
||||
url: session.url,
|
||||
sessionId: session.id,
|
||||
clientSecret: session.client_secret,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return NextResponse.json({ error: 'Error creating checkout session' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import Stripe from 'stripe';
|
||||
import { NextResponse } from 'next/server';
|
||||
import { getStripe } from '@/libs/stripe/server';
|
||||
import { UserPlan } from '@/types/user';
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
const stripe = getStripe();
|
||||
const prices = await stripe.prices.list({
|
||||
expand: ['data.product'],
|
||||
active: true,
|
||||
type: 'recurring',
|
||||
});
|
||||
|
||||
const plans = prices.data
|
||||
.filter((price) => {
|
||||
const product = price.product as Stripe.Product;
|
||||
return product.active === true;
|
||||
})
|
||||
.map((price) => {
|
||||
const product = price.product as Stripe.Product & {
|
||||
metadata: { plan: UserPlan };
|
||||
};
|
||||
return {
|
||||
plan: product.metadata.plan,
|
||||
price_id: price.id,
|
||||
price: price.unit_amount,
|
||||
currency: price.currency,
|
||||
interval: price.recurring?.interval,
|
||||
product: price.product,
|
||||
productName: product.name,
|
||||
};
|
||||
});
|
||||
|
||||
return NextResponse.json(plans);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return NextResponse.json({ error: 'Error fetching subscription plans' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { getStripe } from '@/libs/stripe/server';
|
||||
import { validateUserAndToken } from '@/utils/access';
|
||||
import { createSupabaseAdminClient } from '@/utils/supabase';
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
const { user, token } = await validateUserAndToken(request.headers.get('authorization'));
|
||||
if (!user || !token) {
|
||||
return NextResponse.json({ error: 'Not authenticated' }, { status: 403 });
|
||||
}
|
||||
|
||||
try {
|
||||
const supabase = createSupabaseAdminClient();
|
||||
const { data: customerData } = await supabase
|
||||
.from('customers')
|
||||
.select('stripe_customer_id')
|
||||
.eq('user_id', user.id)
|
||||
.single();
|
||||
|
||||
if (!customerData?.stripe_customer_id) {
|
||||
throw new Error('Customer not found');
|
||||
}
|
||||
|
||||
const stripe = getStripe();
|
||||
const session = await stripe.billingPortal.sessions.create({
|
||||
customer: customerData.stripe_customer_id,
|
||||
return_url: `${request.headers.get('origin')}/user`,
|
||||
});
|
||||
|
||||
return NextResponse.json({ url: session.url });
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return NextResponse.json({ error: 'Error creating portal session' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
import Stripe from 'stripe';
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { getStripe } from '@/libs/stripe/server';
|
||||
import { createOrUpdateSubscription } from '@/utils/stripe';
|
||||
import { createSupabaseAdminClient } from '@/utils/supabase';
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const body = await request.text();
|
||||
const signature = request.headers.get('stripe-signature');
|
||||
|
||||
if (!signature) {
|
||||
return NextResponse.json({ error: 'Missing Stripe signature' }, { status: 401 });
|
||||
}
|
||||
|
||||
const stripe = getStripe();
|
||||
|
||||
let event;
|
||||
try {
|
||||
event = stripe.webhooks.constructEvent(
|
||||
body,
|
||||
signature,
|
||||
process.env['STRIPE_WEBHOOK_SECRET']!,
|
||||
);
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : 'Unknown error';
|
||||
console.error(`Webhook signature verification failed: ${message}`);
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: `Webhook signature verification failed: ${message}`,
|
||||
},
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
switch (event.type) {
|
||||
case 'checkout.session.completed':
|
||||
const session = event.data.object;
|
||||
const userId = session.metadata?.['userId'];
|
||||
if (userId) {
|
||||
if (session.mode === 'subscription') {
|
||||
await handleSuccessfulSubscription(session, userId);
|
||||
} else {
|
||||
await handleSuccessfulPayment(session, userId);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'invoice.payment_succeeded':
|
||||
await handleSuccessfulInvoice(event.data.object);
|
||||
break;
|
||||
|
||||
case 'invoice.payment_failed':
|
||||
await handleFailedInvoice(event.data.object);
|
||||
break;
|
||||
|
||||
case 'customer.subscription.updated':
|
||||
await handleSubscriptionUpdated(event.data.object);
|
||||
break;
|
||||
|
||||
case 'customer.subscription.deleted':
|
||||
await handleSubscriptionCancelled(event.data.object);
|
||||
break;
|
||||
}
|
||||
|
||||
return NextResponse.json({ received: true });
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : 'Unknown error';
|
||||
console.error('Webhook error:', message);
|
||||
return NextResponse.json({ error: message }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSuccessfulPayment(session: Stripe.Checkout.Session, userId: string) {
|
||||
const supabase = createSupabaseAdminClient();
|
||||
await supabase.from('payments').insert({
|
||||
user_id: userId,
|
||||
stripe_checkout_id: session.id,
|
||||
amount: session.amount_total,
|
||||
currency: session.currency,
|
||||
status: 'completed',
|
||||
payment_intent: session.payment_intent,
|
||||
payment_method: session.payment_method_types?.[0] || 'unknown',
|
||||
created_at: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
|
||||
async function handleSuccessfulSubscription(session: Stripe.Checkout.Session, userId: string) {
|
||||
const customerId = session.customer as string;
|
||||
const subscriptionId = session.subscription as string;
|
||||
|
||||
await createOrUpdateSubscription(userId, customerId, subscriptionId);
|
||||
}
|
||||
|
||||
async function handleSuccessfulInvoice(invoice: Stripe.Invoice) {
|
||||
const customerId = invoice.customer;
|
||||
const subscriptionId = invoice.parent?.subscription_details?.subscription;
|
||||
|
||||
if (!subscriptionId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const supabase = createSupabaseAdminClient();
|
||||
const { data: customerData } = await supabase
|
||||
.from('customers')
|
||||
.select('user_id')
|
||||
.eq('stripe_customer_id', customerId)
|
||||
.single();
|
||||
|
||||
if (!customerData?.user_id) {
|
||||
console.error('Customer not found:', customerId);
|
||||
return;
|
||||
}
|
||||
|
||||
await supabase
|
||||
.from('subscriptions')
|
||||
.update({
|
||||
status: 'active',
|
||||
current_period_end: new Date(invoice.lines.data[0]!.period.end * 1000).toISOString(),
|
||||
})
|
||||
.eq('stripe_subscription_id', subscriptionId);
|
||||
|
||||
await supabase
|
||||
.from('plans')
|
||||
.update({
|
||||
status: 'active',
|
||||
})
|
||||
.eq('user_id', customerData.user_id);
|
||||
}
|
||||
|
||||
async function handleFailedInvoice(invoice: Stripe.Invoice) {
|
||||
const customerId = invoice.customer;
|
||||
const subscriptionId = invoice.parent?.subscription_details?.subscription;
|
||||
|
||||
if (!subscriptionId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const supabase = createSupabaseAdminClient();
|
||||
const { data: customerData } = await supabase
|
||||
.from('customers')
|
||||
.select('user_id')
|
||||
.eq('stripe_customer_id', customerId)
|
||||
.single();
|
||||
|
||||
if (!customerData?.user_id) {
|
||||
console.error('Customer not found:', customerId);
|
||||
return;
|
||||
}
|
||||
|
||||
await supabase
|
||||
.from('subscriptions')
|
||||
.update({
|
||||
status: 'past_due',
|
||||
})
|
||||
.eq('stripe_subscription_id', subscriptionId);
|
||||
|
||||
await supabase
|
||||
.from('plans')
|
||||
.update({
|
||||
status: 'past_due',
|
||||
})
|
||||
.eq('id', customerData.user_id);
|
||||
}
|
||||
|
||||
async function handleSubscriptionUpdated(subscription: Stripe.Subscription) {
|
||||
const subscriptionId = subscription.id;
|
||||
|
||||
const supabase = createSupabaseAdminClient();
|
||||
const { data: subscriptionData } = await supabase
|
||||
.from('subscriptions')
|
||||
.select('user_id, stripe_customer_id')
|
||||
.eq('stripe_subscription_id', subscriptionId)
|
||||
.single();
|
||||
|
||||
if (!subscriptionData) {
|
||||
console.error('Subscription not found:', subscriptionId);
|
||||
return;
|
||||
}
|
||||
const { user_id, stripe_customer_id } = subscriptionData;
|
||||
await createOrUpdateSubscription(user_id, stripe_customer_id, subscriptionId);
|
||||
}
|
||||
|
||||
async function handleSubscriptionCancelled(subscription: Stripe.Subscription) {
|
||||
const subscriptionId = subscription.id;
|
||||
|
||||
const supabase = createSupabaseAdminClient();
|
||||
await supabase
|
||||
.from('subscriptions')
|
||||
.update({
|
||||
status: 'cancelled',
|
||||
cancelled_at: new Date().toISOString(),
|
||||
})
|
||||
.eq('stripe_subscription_id', subscriptionId);
|
||||
|
||||
const { data: subscriptionData } = await supabase
|
||||
.from('subscriptions')
|
||||
.select('user_id')
|
||||
.eq('stripe_subscription_id', subscriptionId)
|
||||
.single();
|
||||
|
||||
if (subscriptionData?.user_id) {
|
||||
await supabase
|
||||
.from('plans')
|
||||
.update({
|
||||
plan: 'free',
|
||||
status: 'cancelled',
|
||||
})
|
||||
.eq('id', subscriptionData.user_id);
|
||||
}
|
||||
}
|
||||
|
||||
// This is needed to parse the body as a stream for the webhook signature verification
|
||||
export const config = {
|
||||
api: {
|
||||
bodyParser: false,
|
||||
},
|
||||
};
|
||||
@@ -1,42 +1,42 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect } from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { handleAuthCallback } from '@/helpers/auth';
|
||||
import { useAuth } from '@/context/AuthContext';
|
||||
import { handleAuthCallback } from '@/helpers/auth';
|
||||
|
||||
export default function AuthCallback() {
|
||||
const router = useRouter();
|
||||
const { login } = useAuth();
|
||||
|
||||
if (typeof window === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
const hash = window.location.hash || '';
|
||||
const params = new URLSearchParams(hash.slice(1));
|
||||
useEffect(() => {
|
||||
const hash = window.location.hash || '';
|
||||
const params = new URLSearchParams(hash.slice(1));
|
||||
|
||||
const accessToken = params.get('access_token');
|
||||
const refreshToken = params.get('refresh_token');
|
||||
const type = params.get('type');
|
||||
const next = params.get('next') ?? '/';
|
||||
const error = params.get('error');
|
||||
const errorDescription = params.get('error_description');
|
||||
const errorCode = params.get('error_code');
|
||||
const accessToken = params.get('access_token');
|
||||
const refreshToken = params.get('refresh_token');
|
||||
const type = params.get('type');
|
||||
const next = params.get('next') ?? '/';
|
||||
const error = params.get('error');
|
||||
const errorDescription = params.get('error_description');
|
||||
const errorCode = params.get('error_code');
|
||||
|
||||
handleAuthCallback({
|
||||
accessToken,
|
||||
refreshToken,
|
||||
type,
|
||||
next,
|
||||
error,
|
||||
errorCode,
|
||||
errorDescription,
|
||||
login,
|
||||
navigate: router.push,
|
||||
});
|
||||
handleAuthCallback({
|
||||
accessToken,
|
||||
refreshToken,
|
||||
type,
|
||||
next,
|
||||
error,
|
||||
errorCode,
|
||||
errorDescription,
|
||||
login,
|
||||
navigate: router.push,
|
||||
});
|
||||
}, [login, router]);
|
||||
|
||||
return (
|
||||
<div className='fixed inset-0 z-50 flex items-center justify-center'>
|
||||
<span className='loading loading-infinity loading-xl w-20'></span>
|
||||
<span className='loading loading-infinity loading-xl w-20' />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -18,14 +18,15 @@ import { useThemeStore } from '@/store/themeStore';
|
||||
import { useSettingsStore } from '@/store/settingsStore';
|
||||
import { useTranslation } from '@/hooks/useTranslation';
|
||||
import { useTrafficLightStore } from '@/store/trafficLightStore';
|
||||
import { isTauriAppPlatform } from '@/services/environment';
|
||||
import { getBaseUrl, isTauriAppPlatform } from '@/services/environment';
|
||||
import { onOpenUrl } from '@tauri-apps/plugin-deep-link';
|
||||
import { start, cancel, onUrl, onInvalidUrl } from '@fabianlars/tauri-plugin-oauth';
|
||||
import { openUrl } from '@tauri-apps/plugin-opener';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { handleAuthCallback } from '@/helpers/auth';
|
||||
import { getUserPlan } from '@/utils/access';
|
||||
import { getAppleIdAuth, Scope } from './utils/appleIdAuth';
|
||||
import { authWithCustomTab, authWithSafari } from './utils/nativeAuth';
|
||||
import { READEST_WEB_BASE_URL } from '@/services/constants';
|
||||
import WindowButtons from '@/components/WindowButtons';
|
||||
|
||||
type OAuthProvider = 'google' | 'apple' | 'azure' | 'github';
|
||||
@@ -42,7 +43,7 @@ interface ProviderLoginProp {
|
||||
label: string;
|
||||
}
|
||||
|
||||
const WEB_AUTH_CALLBACK = `${READEST_WEB_BASE_URL}/auth/callback`;
|
||||
const WEB_AUTH_CALLBACK = `${getBaseUrl()}/auth/callback`;
|
||||
const DEEPLINK_CALLBACK = 'readest://auth-callback';
|
||||
const USE_APPLE_SIGN_IN = process.env['NEXT_PUBLIC_USE_APPLE_SIGN_IN'] === 'true';
|
||||
|
||||
@@ -70,15 +71,22 @@ export default function AuthPage() {
|
||||
const { isTrafficLightVisible } = useTrafficLightStore();
|
||||
const { settings, setSettings, saveSettings } = useSettingsStore();
|
||||
const [port, setPort] = useState<number | null>(null);
|
||||
const isOAuthServerRunning = useRef(false);
|
||||
const [isMounted, setIsMounted] = useState(false);
|
||||
const isOAuthServerRunning = useRef(false);
|
||||
const useCustomeOAuth = useRef(false);
|
||||
|
||||
const headerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useTheme({ systemUIVisible: false });
|
||||
|
||||
const getTauriRedirectTo = (isOAuth: boolean) => {
|
||||
if (process.env.NODE_ENV === 'production' || appService?.isMobileApp || USE_APPLE_SIGN_IN) {
|
||||
// For custom OAuth mode, use a local server to handle the OAuth callback
|
||||
// This is useful for development or some sandboxed environments like Flatpak
|
||||
// where custom URL schemes are not supported
|
||||
if (
|
||||
!useCustomeOAuth.current &&
|
||||
(process.env.NODE_ENV === 'production' || appService?.isMobileApp || USE_APPLE_SIGN_IN)
|
||||
) {
|
||||
if (appService?.isMobileApp) {
|
||||
return isOAuth ? DEEPLINK_CALLBACK : WEB_AUTH_CALLBACK;
|
||||
}
|
||||
@@ -163,8 +171,11 @@ export default function AuthPage() {
|
||||
const accessToken = params.get('access_token');
|
||||
const refreshToken = params.get('refresh_token');
|
||||
const type = params.get('type');
|
||||
const next = params.get('next') ?? '/';
|
||||
if (accessToken) {
|
||||
let next = params.get('next') ?? '/';
|
||||
if (getUserPlan(accessToken) === 'free') {
|
||||
next = '/user';
|
||||
}
|
||||
handleAuthCallback({ accessToken, refreshToken, type, next, login, navigate: router.push });
|
||||
}
|
||||
}
|
||||
@@ -172,7 +183,10 @@ export default function AuthPage() {
|
||||
|
||||
const startTauriOAuth = async () => {
|
||||
try {
|
||||
if (process.env.NODE_ENV === 'production' || appService?.isMobileApp || USE_APPLE_SIGN_IN) {
|
||||
if (
|
||||
!useCustomeOAuth.current &&
|
||||
(process.env.NODE_ENV === 'production' || appService?.isMobileApp || USE_APPLE_SIGN_IN)
|
||||
) {
|
||||
const { getCurrentWindow } = await import('@tauri-apps/api/window');
|
||||
const currentWindow = getCurrentWindow();
|
||||
currentWindow.listen('single-instance', ({ event, payload }) => {
|
||||
@@ -242,7 +256,7 @@ export default function AuthPage() {
|
||||
button_label: _('Sign up'),
|
||||
loading_button_label: _('Signing up...'),
|
||||
social_provider_text: _('Sign in with {{provider}}'),
|
||||
link_text: _('Don’t have an account? Sign up'),
|
||||
link_text: _("Don't have an account? Sign up"),
|
||||
confirmation_text: _('Check your email for the confirmation link'),
|
||||
},
|
||||
magic_link: {
|
||||
@@ -281,6 +295,12 @@ export default function AuthPage() {
|
||||
if (isOAuthServerRunning.current) return;
|
||||
isOAuthServerRunning.current = true;
|
||||
|
||||
invoke('get_environment_variable', { name: 'USE_CUSTOM_OAUTH' }).then((value) => {
|
||||
if (value === 'true') {
|
||||
useCustomeOAuth.current = true;
|
||||
}
|
||||
});
|
||||
|
||||
startTauriOAuth();
|
||||
return () => {
|
||||
isOAuthServerRunning.current = false;
|
||||
@@ -294,7 +314,13 @@ export default function AuthPage() {
|
||||
if (session?.access_token && session.user) {
|
||||
login(session.access_token, session.user);
|
||||
const redirectTo = new URLSearchParams(window.location.search).get('redirect');
|
||||
router.push(redirectTo ?? '/library');
|
||||
const lastRedirectAtKey = 'lastRedirectAt';
|
||||
const lastRedirectAt = parseInt(localStorage.getItem(lastRedirectAtKey) || '0', 10);
|
||||
const now = Date.now();
|
||||
localStorage.setItem(lastRedirectAtKey, now.toString());
|
||||
if (now - lastRedirectAt > 3000) {
|
||||
router.push(redirectTo ?? '/library');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -318,64 +344,82 @@ export default function AuthPage() {
|
||||
return isTauriAppPlatform() ? (
|
||||
<div
|
||||
className={clsx(
|
||||
'fixed inset-0 z-0 flex select-none flex-col items-center overflow-y-auto',
|
||||
'bg-base-100 border-base-200 border',
|
||||
appService?.hasSafeAreaInset && 'pt-[env(safe-area-inset-top)]',
|
||||
'bg-base-100 inset-0 flex select-none flex-col items-center overflow-hidden',
|
||||
appService?.isIOSApp ? 'h-[100vh]' : 'h-dvh',
|
||||
appService?.isLinuxApp && 'window-border',
|
||||
appService?.hasRoundedWindow && 'rounded-window',
|
||||
)}
|
||||
>
|
||||
<div
|
||||
ref={headerRef}
|
||||
className={clsx(
|
||||
'fixed z-10 flex w-full items-center justify-between py-2 pe-6 ps-4',
|
||||
appService?.hasTrafficLight && 'pt-11',
|
||||
'flex h-full w-full flex-col items-center overflow-y-auto',
|
||||
appService?.hasSafeAreaInset && 'pt-[env(safe-area-inset-top)]',
|
||||
)}
|
||||
>
|
||||
<button onClick={handleGoBack} className={clsx('btn btn-ghost h-8 min-h-8 w-8 p-0')}>
|
||||
<IoArrowBack className='text-base-content' />
|
||||
</button>
|
||||
<div
|
||||
ref={headerRef}
|
||||
className={clsx(
|
||||
'fixed z-10 flex w-full items-center justify-between py-2 pe-6 ps-4',
|
||||
appService?.hasTrafficLight && 'pt-11',
|
||||
)}
|
||||
>
|
||||
<button
|
||||
onClick={handleGoBack}
|
||||
className={clsx('btn btn-ghost h-12 min-h-12 w-12 p-0 sm:h-8 sm:min-h-8 sm:w-8')}
|
||||
>
|
||||
<IoArrowBack className='text-base-content' />
|
||||
</button>
|
||||
|
||||
{appService?.hasWindowBar && (
|
||||
<WindowButtons
|
||||
headerRef={headerRef}
|
||||
showMinimize={!isTrafficLightVisible}
|
||||
showMaximize={!isTrafficLightVisible}
|
||||
showClose={!isTrafficLightVisible}
|
||||
onClose={handleGoBack}
|
||||
{appService?.hasWindowBar && (
|
||||
<WindowButtons
|
||||
headerRef={headerRef}
|
||||
showMinimize={!isTrafficLightVisible}
|
||||
showMaximize={!isTrafficLightVisible}
|
||||
showClose={!isTrafficLightVisible}
|
||||
onClose={handleGoBack}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className={clsx(
|
||||
'z-20 flex flex-col items-center pb-8',
|
||||
appService?.hasTrafficLight ? 'mt-24' : 'mt-12',
|
||||
)}
|
||||
style={{ maxWidth: '420px' }}
|
||||
>
|
||||
<ProviderLogin
|
||||
provider='google'
|
||||
handleSignIn={tauriSignIn}
|
||||
Icon={FcGoogle}
|
||||
label={_('Sign in with Google')}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className={clsx('z-20 pb-8', appService?.hasTrafficLight ? 'mt-24' : 'mt-12')}
|
||||
style={{ maxWidth: '420px' }}
|
||||
>
|
||||
<ProviderLogin
|
||||
provider='google'
|
||||
handleSignIn={tauriSignIn}
|
||||
Icon={FcGoogle}
|
||||
label={_('Sign in with Google')}
|
||||
/>
|
||||
<ProviderLogin
|
||||
provider='apple'
|
||||
handleSignIn={appService?.isIOSApp || USE_APPLE_SIGN_IN ? tauriSignInApple : tauriSignIn}
|
||||
Icon={FaApple}
|
||||
label={_('Sign in with Apple')}
|
||||
/>
|
||||
<ProviderLogin
|
||||
provider='github'
|
||||
handleSignIn={tauriSignIn}
|
||||
Icon={FaGithub}
|
||||
label={_('Sign in with GitHub')}
|
||||
/>
|
||||
<hr className='border-base-300 my-3 mt-6 w-64 border-t' />
|
||||
<Auth
|
||||
supabaseClient={supabase}
|
||||
appearance={{ theme: ThemeSupa }}
|
||||
theme={isDarkMode ? 'dark' : 'light'}
|
||||
magicLink={true}
|
||||
providers={[]}
|
||||
redirectTo={getTauriRedirectTo(false)}
|
||||
localization={getAuthLocalization()}
|
||||
/>
|
||||
<ProviderLogin
|
||||
provider='apple'
|
||||
handleSignIn={
|
||||
appService?.isIOSApp || USE_APPLE_SIGN_IN ? tauriSignInApple : tauriSignIn
|
||||
}
|
||||
Icon={FaApple}
|
||||
label={_('Sign in with Apple')}
|
||||
/>
|
||||
<ProviderLogin
|
||||
provider='github'
|
||||
handleSignIn={tauriSignIn}
|
||||
Icon={FaGithub}
|
||||
label={_('Sign in with GitHub')}
|
||||
/>
|
||||
<hr className='border-base-300 my-3 mt-6 w-64 border-t' />
|
||||
<div className='w-full'>
|
||||
<Auth
|
||||
supabaseClient={supabase}
|
||||
appearance={{ theme: ThemeSupa }}
|
||||
theme={isDarkMode ? 'dark' : 'light'}
|
||||
magicLink={true}
|
||||
providers={[]}
|
||||
redirectTo={getTauriRedirectTo(false)}
|
||||
localization={getAuthLocalization()}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
|
||||
@@ -5,6 +5,7 @@ import { useEffect, useState } from 'react';
|
||||
import { useEnv } from '@/context/EnvContext';
|
||||
import { useTranslation } from '@/hooks/useTranslation';
|
||||
import { parseWebViewVersion } from '@/utils/ua';
|
||||
import { handleGlobalError } from '@/utils/error';
|
||||
|
||||
interface ErrorPageProps {
|
||||
error: Error & { digest?: string };
|
||||
@@ -19,7 +20,8 @@ export default function Error({ error, reset }: ErrorPageProps) {
|
||||
useEffect(() => {
|
||||
setBrowserInfo(parseWebViewVersion(appService));
|
||||
posthog.captureException(error);
|
||||
}, [error]);
|
||||
handleGlobalError(error);
|
||||
}, [appService, error]);
|
||||
|
||||
const handleGoHome = () => {
|
||||
window.location.href = '/library';
|
||||
@@ -36,8 +38,8 @@ export default function Error({ error, reset }: ErrorPageProps) {
|
||||
return (
|
||||
<div className='hero bg-base-200 min-h-screen'>
|
||||
<div className='hero-content text-center'>
|
||||
<div className='max-w-2xl'>
|
||||
<div className='mb-8'>
|
||||
<div className='w-full max-w-2xl p-1'>
|
||||
<div className='mb-8 mt-6'>
|
||||
<div className='text-error animate-pulse text-8xl'>⚠️</div>
|
||||
</div>
|
||||
|
||||
@@ -55,8 +57,8 @@ export default function Error({ error, reset }: ErrorPageProps) {
|
||||
<p className='break-words font-mono text-sm'>{error.message}</p>
|
||||
{browserInfo && <p className='mt-2 font-mono text-sm'>Browser: {browserInfo}</p>}
|
||||
{error.stack && (
|
||||
<p className='mt-2 break-words font-mono text-sm'>
|
||||
{error.stack.split('\n').slice(0, 5).join('\n')}
|
||||
<p className='mt-2 whitespace-pre-wrap break-all font-mono text-sm'>
|
||||
{error.stack.split('\n').slice(0, 3).join('\n')}
|
||||
</p>
|
||||
)}
|
||||
{error.digest && <p className='mt-2 text-xs opacity-70'>Error ID: {error.digest}</p>}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { EnvProvider } from '@/context/EnvContext';
|
||||
import Providers from '@/components/Providers';
|
||||
|
||||
import '../styles/globals.css';
|
||||
@@ -69,7 +70,9 @@ export default function RootLayout({ children }: { children: React.ReactNode })
|
||||
<meta name='twitter:image' content={previewImage} />
|
||||
</head>
|
||||
<body>
|
||||
<Providers>{children}</Providers>
|
||||
<EnvProvider>
|
||||
<Providers>{children}</Providers>
|
||||
</EnvProvider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
||||
@@ -11,17 +11,18 @@ import { useEnv } from '@/context/EnvContext';
|
||||
import { useAuth } from '@/context/AuthContext';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useResponsiveSize } from '@/hooks/useResponsiveSize';
|
||||
import { LibraryViewModeType } from '@/types/settings';
|
||||
import { LibraryCoverFitType, LibraryViewModeType } from '@/types/settings';
|
||||
import { navigateToLogin } from '@/utils/nav';
|
||||
import { formatAuthors } from '@/utils/book';
|
||||
import ReadingProgress from './ReadingProgress';
|
||||
import BookCover from '@/components/BookCover';
|
||||
|
||||
interface BookItemProps {
|
||||
mode: LibraryViewModeType;
|
||||
book: Book;
|
||||
mode: LibraryViewModeType;
|
||||
coverFit: LibraryCoverFitType;
|
||||
isSelectMode: boolean;
|
||||
selectedBooks: string[];
|
||||
bookSelected: boolean;
|
||||
transferProgress: number | null;
|
||||
handleBookUpload: (book: Book) => void;
|
||||
handleBookDownload: (book: Book) => void;
|
||||
@@ -29,10 +30,11 @@ interface BookItemProps {
|
||||
}
|
||||
|
||||
const BookItem: React.FC<BookItemProps> = ({
|
||||
mode,
|
||||
book,
|
||||
mode,
|
||||
coverFit,
|
||||
isSelectMode,
|
||||
selectedBooks,
|
||||
bookSelected,
|
||||
transferProgress,
|
||||
handleBookUpload,
|
||||
handleBookDownload,
|
||||
@@ -52,24 +54,26 @@ const BookItem: React.FC<BookItemProps> = ({
|
||||
<div
|
||||
className={clsx(
|
||||
'book-item flex',
|
||||
mode === 'grid' && 'h-full flex-col',
|
||||
mode === 'grid' && 'h-full flex-col justify-end',
|
||||
mode === 'list' && 'h-28 flex-row gap-4 overflow-hidden',
|
||||
appService?.hasContextMenu ? 'cursor-pointer' : '',
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={clsx(
|
||||
'bg-base-100 relative flex aspect-[28/41] items-center justify-center overflow-hidden shadow-md',
|
||||
mode === 'list' && 'min-w-20',
|
||||
'relative flex aspect-[28/41] justify-center',
|
||||
coverFit === 'crop' && 'overflow-hidden shadow-md',
|
||||
mode === 'grid' && 'items-end',
|
||||
mode === 'list' && 'min-w-20 items-center',
|
||||
)}
|
||||
>
|
||||
<BookCover mode={mode} book={book} />
|
||||
{selectedBooks.includes(book.hash) && (
|
||||
<BookCover mode={mode} book={book} coverFit={coverFit} showSpine={false} />
|
||||
{bookSelected && (
|
||||
<div className='absolute inset-0 bg-black opacity-30 transition-opacity duration-300'></div>
|
||||
)}
|
||||
{isSelectMode && (
|
||||
<div className='absolute bottom-1 right-1'>
|
||||
{selectedBooks.includes(book.hash) ? (
|
||||
{bookSelected ? (
|
||||
<MdCheckCircle className='fill-blue-500' />
|
||||
) : (
|
||||
<MdCheckCircleOutline className='fill-gray-300 drop-shadow-sm' />
|
||||
@@ -102,6 +106,10 @@ const BookItem: React.FC<BookItemProps> = ({
|
||||
</div>
|
||||
<div
|
||||
className={clsx('flex items-center', book.progress ? 'justify-between' : 'justify-end')}
|
||||
style={{
|
||||
height: `${iconSize15}px`,
|
||||
minHeight: `${iconSize15}px`,
|
||||
}}
|
||||
>
|
||||
{book.progress && <ReadingProgress book={book} />}
|
||||
<div className='flex items-center justify-center gap-x-2'>
|
||||
@@ -115,7 +123,7 @@ const BookItem: React.FC<BookItemProps> = ({
|
||||
onPointerLeave={(e) => stopEvent(e)}
|
||||
onClick={() => showBookDetailsModal(book)}
|
||||
>
|
||||
<div className='pt-[1px]'>
|
||||
<div className='pt-[2px] sm:pt-[1px]'>
|
||||
<LiaInfoCircleSolid size={iconSize15} />
|
||||
</div>
|
||||
</button>
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import clsx from 'clsx';
|
||||
import * as React from 'react';
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { MdDelete, MdOpenInNew, MdOutlineCancel, MdInfoOutline } from 'react-icons/md';
|
||||
import { LuFolderPlus } from 'react-icons/lu';
|
||||
import { PiPlus } from 'react-icons/pi';
|
||||
import { Book, BooksGroup } from '@/types/book';
|
||||
import { LibraryViewModeType } from '@/types/settings';
|
||||
import { LibraryCoverFitType, LibraryViewModeType } from '@/types/settings';
|
||||
import { useEnv } from '@/context/EnvContext';
|
||||
import { useSettingsStore } from '@/store/settingsStore';
|
||||
import { useLibraryStore } from '@/store/libraryStore';
|
||||
import { useTranslation } from '@/hooks/useTranslation';
|
||||
import { navigateToLibrary, navigateToReader } from '@/utils/nav';
|
||||
import { navigateToLibrary, navigateToReader, showReaderWindow } from '@/utils/nav';
|
||||
import { formatAuthors, formatTitle } from '@/utils/book';
|
||||
import { isMd5 } from '@/utils/md5';
|
||||
|
||||
@@ -54,7 +54,6 @@ const Bookshelf: React.FC<BookshelfProps> = ({
|
||||
const { appService } = useEnv();
|
||||
const { settings } = useSettingsStore();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [selectedBooks, setSelectedBooks] = useState<string[]>([]);
|
||||
const [showSelectModeActions, setShowSelectModeActions] = useState(false);
|
||||
const [showDeleteAlert, setShowDeleteAlert] = useState(false);
|
||||
const [showGroupingModal, setShowGroupingModal] = useState(false);
|
||||
@@ -66,9 +65,11 @@ const Bookshelf: React.FC<BookshelfProps> = ({
|
||||
const [sortOrder, setSortOrder] = useState(
|
||||
searchParams?.get('order') || (settings.librarySortAscending ? 'asc' : 'desc'),
|
||||
);
|
||||
const [coverFit, setCoverFit] = useState(searchParams?.get('cover') || settings.libraryCoverFit);
|
||||
const isImportingBook = useRef(false);
|
||||
|
||||
const { setCurrentBookshelf, setLibrary } = useLibraryStore();
|
||||
const { setSelectedBooks, getSelectedBooks, toggleSelectedBook } = useLibraryStore();
|
||||
const allBookshelfItems =
|
||||
viewMode === 'grid' ? generateGridItems(libraryBooks) : generateListItems(libraryBooks);
|
||||
|
||||
@@ -106,6 +107,7 @@ const Bookshelf: React.FC<BookshelfProps> = ({
|
||||
const view = searchParams?.get('view') || settings.libraryViewMode;
|
||||
const sort = searchParams?.get('sort') || settings.librarySortBy;
|
||||
const order = searchParams?.get('order') || (settings.librarySortAscending ? 'asc' : 'desc');
|
||||
const cover = searchParams?.get('cover') || settings.libraryCoverFit;
|
||||
const params = new URLSearchParams(searchParams?.toString());
|
||||
if (query) {
|
||||
params.set('q', query);
|
||||
@@ -132,6 +134,10 @@ const Bookshelf: React.FC<BookshelfProps> = ({
|
||||
} else {
|
||||
params.delete('view');
|
||||
}
|
||||
setCoverFit(cover);
|
||||
if (cover === 'crop') {
|
||||
params.delete('cover');
|
||||
}
|
||||
if (sort === 'updated' && order === 'desc' && view === 'grid') {
|
||||
params.delete('sort');
|
||||
params.delete('order');
|
||||
@@ -156,18 +162,24 @@ const Bookshelf: React.FC<BookshelfProps> = ({
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [searchParams, libraryBooks, showGroupingModal]);
|
||||
|
||||
const toggleSelection = (id: string) => {
|
||||
setSelectedBooks((prev) =>
|
||||
prev.includes(id) ? prev.filter((selectedId) => selectedId !== id) : [...prev, id],
|
||||
);
|
||||
};
|
||||
const toggleSelection = useCallback((id: string) => {
|
||||
toggleSelectedBook(id);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const openSelectedBooks = () => {
|
||||
setTimeout(() => setLoading(true), 200);
|
||||
navigateToReader(router, selectedBooks);
|
||||
handleSetSelectMode(false);
|
||||
if (appService?.hasWindow && settings.openBookInNewWindow) {
|
||||
showReaderWindow(appService, getSelectedBooks());
|
||||
} else {
|
||||
setTimeout(() => setLoading(true), 200);
|
||||
navigateToReader(router, getSelectedBooks());
|
||||
}
|
||||
};
|
||||
|
||||
const openBookDetails = () => {
|
||||
handleSetSelectMode(false);
|
||||
const selectedBooks = getSelectedBooks();
|
||||
const book = libraryBooks.find((book) => book.hash === selectedBooks[0]);
|
||||
if (book) {
|
||||
handleShowDetailsBook(book);
|
||||
@@ -176,6 +188,7 @@ const Bookshelf: React.FC<BookshelfProps> = ({
|
||||
|
||||
const getBooksToDelete = () => {
|
||||
const booksToDelete: Book[] = [];
|
||||
const selectedBooks = getSelectedBooks();
|
||||
selectedBooks.forEach((id) => {
|
||||
for (const book of libraryBooks.filter((book) => book.hash === id || book.groupId === id)) {
|
||||
if (book && !book.deletedAt) {
|
||||
@@ -210,7 +223,13 @@ const Bookshelf: React.FC<BookshelfProps> = ({
|
||||
const searchTerm = new RegExp(queryTerm, 'i');
|
||||
const title = formatTitle(item.title);
|
||||
const authors = formatAuthors(item.author);
|
||||
return searchTerm.test(title) || searchTerm.test(authors);
|
||||
return (
|
||||
searchTerm.test(title) ||
|
||||
searchTerm.test(authors) ||
|
||||
searchTerm.test(item.format) ||
|
||||
(item.groupName && searchTerm.test(item.groupName)) ||
|
||||
(item.metadata?.description && searchTerm.test(item.metadata?.description))
|
||||
);
|
||||
};
|
||||
const bookSorter = (a: Book, b: Book) => {
|
||||
const uiLanguage = localStorage?.getItem('i18nextLng') || '';
|
||||
@@ -275,23 +294,28 @@ const Bookshelf: React.FC<BookshelfProps> = ({
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isSelectMode, isSelectAll, isSelectNone]);
|
||||
|
||||
const selectedBooks = getSelectedBooks();
|
||||
|
||||
return (
|
||||
<div className='bookshelf'>
|
||||
<div
|
||||
className={clsx(
|
||||
'transform-wrapper',
|
||||
'bookshelf-items transform-wrapper',
|
||||
viewMode === 'grid' && 'grid flex-1 grid-cols-3 gap-x-4 px-4 sm:gap-x-0 sm:px-2',
|
||||
viewMode === 'grid' && 'sm:grid-cols-4 md:grid-cols-6 xl:grid-cols-8 2xl:grid-cols-12',
|
||||
viewMode === 'list' && 'flex flex-col',
|
||||
)}
|
||||
>
|
||||
{filteredBookshelfItems.map((item, index) => (
|
||||
{filteredBookshelfItems.map((item) => (
|
||||
<BookshelfItem
|
||||
key={`library-item-${index}`}
|
||||
mode={viewMode as LibraryViewModeType}
|
||||
key={`library-item-${'hash' in item ? item.hash : item.id}`}
|
||||
item={item}
|
||||
mode={viewMode as LibraryViewModeType}
|
||||
coverFit={coverFit as LibraryCoverFitType}
|
||||
isSelectMode={isSelectMode}
|
||||
selectedBooks={selectedBooks}
|
||||
itemSelected={
|
||||
'hash' in item ? selectedBooks.includes(item.hash) : selectedBooks.includes(item.id)
|
||||
}
|
||||
setLoading={setLoading}
|
||||
toggleSelection={toggleSelection}
|
||||
handleBookUpload={handleBookUpload}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import clsx from 'clsx';
|
||||
import { useCallback } from 'react';
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import { navigateToLibrary, navigateToReader } from '@/utils/nav';
|
||||
import { navigateToLibrary, navigateToReader, showReaderWindow } from '@/utils/nav';
|
||||
import { useEnv } from '@/context/EnvContext';
|
||||
import { useLibraryStore } from '@/store/libraryStore';
|
||||
import { useSettingsStore } from '@/store/settingsStore';
|
||||
@@ -8,9 +9,10 @@ import { useTranslation } from '@/hooks/useTranslation';
|
||||
import { useLongPress } from '@/hooks/useLongPress';
|
||||
import { Menu, MenuItem } from '@tauri-apps/api/menu';
|
||||
import { revealItemInDir } from '@tauri-apps/plugin-opener';
|
||||
import { getOSPlatform } from '@/utils/misc';
|
||||
import { getLocalBookFilename } from '@/utils/book';
|
||||
import { LibraryViewModeType } from '@/types/settings';
|
||||
import { getOSPlatform } from '@/utils/misc';
|
||||
import { throttle } from '@/utils/throttle';
|
||||
import { LibraryCoverFitType, LibraryViewModeType } from '@/types/settings';
|
||||
import { BOOK_UNGROUPED_ID, BOOK_UNGROUPED_NAME } from '@/services/constants';
|
||||
import { FILE_REVEAL_LABELS, FILE_REVEAL_PLATFORMS } from '@/utils/os';
|
||||
import { Book, BookGroupType, BooksGroup } from '@/types/book';
|
||||
@@ -73,8 +75,9 @@ export const generateGroupsList = (items: Book[]): BookGroupType[] => {
|
||||
interface BookshelfItemProps {
|
||||
mode: LibraryViewModeType;
|
||||
item: BookshelfItem;
|
||||
coverFit: LibraryCoverFitType;
|
||||
isSelectMode: boolean;
|
||||
selectedBooks: string[];
|
||||
itemSelected: boolean;
|
||||
transferProgress: number | null;
|
||||
setLoading: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
toggleSelection: (hash: string) => void;
|
||||
@@ -88,8 +91,9 @@ interface BookshelfItemProps {
|
||||
const BookshelfItem: React.FC<BookshelfItemProps> = ({
|
||||
mode,
|
||||
item,
|
||||
coverFit,
|
||||
isSelectMode,
|
||||
selectedBooks,
|
||||
itemSelected,
|
||||
transferProgress,
|
||||
setLoading,
|
||||
toggleSelection,
|
||||
@@ -106,22 +110,28 @@ const BookshelfItem: React.FC<BookshelfItemProps> = ({
|
||||
const { settings } = useSettingsStore();
|
||||
const { updateBook } = useLibraryStore();
|
||||
|
||||
const showBookDetailsModal = async (book: Book) => {
|
||||
const showBookDetailsModal = useCallback(async (book: Book) => {
|
||||
if (await makeBookAvailable(book)) {
|
||||
handleShowDetailsBook(book);
|
||||
}
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const makeBookAvailable = async (book: Book) => {
|
||||
if (book.uploadedAt && !book.downloadedAt) {
|
||||
if (await appService?.isBookAvailable(book)) {
|
||||
if (!book.downloadedAt || !book.coverDownloadedAt) {
|
||||
book.downloadedAt = Date.now();
|
||||
book.coverDownloadedAt = Date.now();
|
||||
await updateBook(envConfig, book);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
let available = false;
|
||||
const loadingTimeout = setTimeout(() => setLoading(true), 200);
|
||||
try {
|
||||
available = await handleBookDownload(book);
|
||||
updateBook(envConfig, book);
|
||||
await updateBook(envConfig, book);
|
||||
} finally {
|
||||
if (loadingTimeout) clearTimeout(loadingTimeout);
|
||||
setLoading(false);
|
||||
@@ -131,24 +141,41 @@ const BookshelfItem: React.FC<BookshelfItemProps> = ({
|
||||
return true;
|
||||
};
|
||||
|
||||
const handleBookClick = async (book: Book) => {
|
||||
if (isSelectMode) {
|
||||
toggleSelection(book.hash);
|
||||
} else {
|
||||
if (!(await makeBookAvailable(book))) return;
|
||||
navigateToReader(router, [book.hash]);
|
||||
}
|
||||
};
|
||||
const handleBookClick = useCallback(
|
||||
async (book: Book) => {
|
||||
if (isSelectMode) {
|
||||
toggleSelection(book.hash);
|
||||
} else {
|
||||
const available = await makeBookAvailable(book);
|
||||
if (!available) return;
|
||||
if (appService?.hasWindow && settings.openBookInNewWindow) {
|
||||
showReaderWindow(appService, [book.hash]);
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
navigateToReader(router, [book.hash]);
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
},
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[isSelectMode, settings.openBookInNewWindow, appService],
|
||||
);
|
||||
|
||||
const handleGroupClick = (group: BooksGroup) => {
|
||||
if (isSelectMode) {
|
||||
toggleSelection(group.id);
|
||||
} else {
|
||||
const params = new URLSearchParams(searchParams?.toString());
|
||||
params.set('group', group.id);
|
||||
navigateToLibrary(router, `${params.toString()}`);
|
||||
}
|
||||
};
|
||||
const handleGroupClick = useCallback(
|
||||
(group: BooksGroup) => {
|
||||
if (isSelectMode) {
|
||||
toggleSelection(group.id);
|
||||
} else {
|
||||
const params = new URLSearchParams(searchParams?.toString());
|
||||
params.set('group', group.id);
|
||||
setTimeout(() => {
|
||||
navigateToLibrary(router, `${params.toString()}`);
|
||||
}, 0);
|
||||
}
|
||||
},
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[isSelectMode, searchParams],
|
||||
);
|
||||
|
||||
const bookContextMenuHandler = async (book: Book) => {
|
||||
if (!appService?.hasContextMenu) return;
|
||||
@@ -156,7 +183,7 @@ const BookshelfItem: React.FC<BookshelfItemProps> = ({
|
||||
const fileRevealLabel =
|
||||
FILE_REVEAL_LABELS[osPlatform as FILE_REVEAL_PLATFORMS] || FILE_REVEAL_LABELS.default;
|
||||
const selectBookMenuItem = await MenuItem.new({
|
||||
text: selectedBooks.includes(book.hash) ? _('Deselect Book') : _('Select Book'),
|
||||
text: itemSelected ? _('Deselect Book') : _('Select Book'),
|
||||
action: async () => {
|
||||
if (!isSelectMode) handleSetSelectMode(true);
|
||||
toggleSelection(book.hash);
|
||||
@@ -210,7 +237,7 @@ const BookshelfItem: React.FC<BookshelfItemProps> = ({
|
||||
const groupContextMenuHandler = async (group: BooksGroup) => {
|
||||
if (!appService?.hasContextMenu) return;
|
||||
const selectGroupMenuItem = await MenuItem.new({
|
||||
text: selectedBooks.includes(group.id) ? _('Deselect Group') : _('Select Group'),
|
||||
text: itemSelected ? _('Deselect Group') : _('Select Group'),
|
||||
action: async () => {
|
||||
if (!isSelectMode) handleSetSelectMode(true);
|
||||
toggleSelection(group.id);
|
||||
@@ -230,8 +257,9 @@ const BookshelfItem: React.FC<BookshelfItemProps> = ({
|
||||
menu.popup();
|
||||
};
|
||||
|
||||
const { pressing, handlers } = useLongPress({
|
||||
onLongPress: async () => {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const handleSelectItem = useCallback(
|
||||
throttle(() => {
|
||||
if (!isSelectMode) {
|
||||
handleSetSelectMode(true);
|
||||
}
|
||||
@@ -240,22 +268,56 @@ const BookshelfItem: React.FC<BookshelfItemProps> = ({
|
||||
} else {
|
||||
toggleSelection((item as BooksGroup).id);
|
||||
}
|
||||
},
|
||||
onTap: () => {
|
||||
}, 100),
|
||||
[isSelectMode],
|
||||
);
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const handleOpenItem = useCallback(
|
||||
throttle(() => {
|
||||
if (isSelectMode) {
|
||||
handleSelectItem();
|
||||
return;
|
||||
}
|
||||
if ('format' in item) {
|
||||
handleBookClick(item as Book);
|
||||
} else {
|
||||
handleGroupClick(item as BooksGroup);
|
||||
}
|
||||
},
|
||||
onContextMenu: () => {
|
||||
}, 100),
|
||||
[handleSelectItem, handleBookClick, handleGroupClick],
|
||||
);
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const handleContextMenu = useCallback(
|
||||
throttle(() => {
|
||||
if ('format' in item) {
|
||||
bookContextMenuHandler(item as Book);
|
||||
} else {
|
||||
groupContextMenuHandler(item as BooksGroup);
|
||||
}
|
||||
}, 100),
|
||||
[itemSelected],
|
||||
);
|
||||
|
||||
const { pressing, handlers } = useLongPress(
|
||||
{
|
||||
onLongPress: () => {
|
||||
handleSelectItem();
|
||||
},
|
||||
onTap: () => {
|
||||
handleOpenItem();
|
||||
},
|
||||
onContextMenu: () => {
|
||||
if (appService?.hasContextMenu) {
|
||||
handleContextMenu();
|
||||
} else if (appService?.isAndroidApp) {
|
||||
handleSelectItem();
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
[isSelectMode, handleSelectItem, handleOpenItem, handleContextMenu],
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={clsx(mode === 'list' && 'sm:hover:bg-base-300/50 px-4 sm:px-6')}>
|
||||
@@ -264,6 +326,7 @@ const BookshelfItem: React.FC<BookshelfItemProps> = ({
|
||||
'group',
|
||||
mode === 'grid' && 'sm:hover:bg-base-300/50 flex h-full flex-col px-0 py-4 sm:px-4',
|
||||
mode === 'list' && 'border-base-300 flex flex-col border-b py-2',
|
||||
appService?.isMobileApp && 'no-context-menu',
|
||||
pressing ? (mode === 'grid' ? 'scale-95' : 'scale-98') : 'scale-100',
|
||||
)}
|
||||
style={{
|
||||
@@ -276,15 +339,16 @@ const BookshelfItem: React.FC<BookshelfItemProps> = ({
|
||||
<BookItem
|
||||
mode={mode}
|
||||
book={item}
|
||||
coverFit={coverFit}
|
||||
isSelectMode={isSelectMode}
|
||||
selectedBooks={selectedBooks}
|
||||
bookSelected={itemSelected}
|
||||
transferProgress={transferProgress}
|
||||
handleBookUpload={handleBookUpload}
|
||||
handleBookDownload={handleBookDownload}
|
||||
showBookDetailsModal={showBookDetailsModal}
|
||||
/>
|
||||
) : (
|
||||
<GroupItem group={item} isSelectMode={isSelectMode} selectedBooks={selectedBooks} />
|
||||
<GroupItem group={item} isSelectMode={isSelectMode} groupSelected={itemSelected} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,21 +1,24 @@
|
||||
import clsx from 'clsx';
|
||||
import { MdCheckCircle, MdCheckCircleOutline } from 'react-icons/md';
|
||||
import { useEnv } from '@/context/EnvContext';
|
||||
import { useResponsiveSize } from '@/hooks/useResponsiveSize';
|
||||
import { BooksGroup } from '@/types/book';
|
||||
import BookCover from '@/components/BookCover';
|
||||
|
||||
interface GroupItemProps {
|
||||
group: BooksGroup;
|
||||
isSelectMode: boolean;
|
||||
selectedBooks: string[];
|
||||
groupSelected: boolean;
|
||||
}
|
||||
|
||||
const GroupItem: React.FC<GroupItemProps> = ({ group, isSelectMode, selectedBooks }) => {
|
||||
const GroupItem: React.FC<GroupItemProps> = ({ group, isSelectMode, groupSelected }) => {
|
||||
const { appService } = useEnv();
|
||||
const iconSize15 = useResponsiveSize(15);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
'group-item flex h-full flex-col',
|
||||
'group-item flex h-full flex-col justify-end',
|
||||
appService?.hasContextMenu ? 'cursor-pointer' : '',
|
||||
)}
|
||||
>
|
||||
@@ -27,12 +30,12 @@ const GroupItem: React.FC<GroupItemProps> = ({ group, isSelectMode, selectedBook
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{selectedBooks.includes(group.id) && (
|
||||
{groupSelected && (
|
||||
<div className='absolute inset-0 bg-black opacity-30 transition-opacity duration-300'></div>
|
||||
)}
|
||||
{isSelectMode && (
|
||||
<div className='absolute bottom-1 right-1'>
|
||||
{selectedBooks.includes(group.id) ? (
|
||||
{groupSelected ? (
|
||||
<MdCheckCircle className='fill-blue-500' />
|
||||
) : (
|
||||
<MdCheckCircleOutline className='fill-gray-300 drop-shadow-sm' />
|
||||
@@ -40,10 +43,13 @@ const GroupItem: React.FC<GroupItemProps> = ({ group, isSelectMode, selectedBook
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className='min-w-0 flex-1 pt-2'>
|
||||
<h4 className='block overflow-hidden text-ellipsis whitespace-nowrap text-xs font-semibold'>
|
||||
{group.name}
|
||||
</h4>
|
||||
<div className={clsx('flex w-full flex-col pt-2')}>
|
||||
<div className='min-w-0 flex-1'>
|
||||
<h4 className='block overflow-hidden text-ellipsis whitespace-nowrap text-xs font-semibold'>
|
||||
{group.name}
|
||||
</h4>
|
||||
</div>
|
||||
<div className='placeholder' style={{ height: `${iconSize15}px` }}></div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,389 @@
|
||||
import React, { useState, useEffect, useMemo, useCallback } from 'react';
|
||||
import { md5 } from 'js-md5';
|
||||
import clsx from 'clsx';
|
||||
import { type as osType } from '@tauri-apps/plugin-os';
|
||||
import Dialog from '@/components/Dialog';
|
||||
import { useTranslation } from '@/hooks/useTranslation';
|
||||
import { useSettingsStore } from '@/store/settingsStore';
|
||||
import { useEnv } from '@/context/EnvContext';
|
||||
import { eventDispatcher } from '@/utils/event';
|
||||
import { KOSyncClient } from '@/services/sync/KOSyncClient';
|
||||
import { KoreaderSyncChecksumMethod, KoreaderSyncStrategy } from '@/types/settings';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { debounce } from '@/utils/debounce';
|
||||
import { getOSPlatform } from '@/utils/misc';
|
||||
|
||||
type Option = {
|
||||
value: string;
|
||||
label: string;
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
type SelectProps = {
|
||||
value: string;
|
||||
onChange: (e: React.ChangeEvent<HTMLSelectElement>) => void;
|
||||
options: Option[];
|
||||
disabled?: boolean;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const StyledSelect: React.FC<SelectProps> = ({
|
||||
value,
|
||||
onChange,
|
||||
options,
|
||||
className,
|
||||
disabled = false,
|
||||
}) => {
|
||||
return (
|
||||
<select
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
className={clsx(
|
||||
'select select-bordered h-12 w-full text-sm focus:outline-none focus:ring-0',
|
||||
className,
|
||||
)}
|
||||
disabled={disabled}
|
||||
>
|
||||
{options.map(({ value, label, disabled = false }) => (
|
||||
<option key={value} value={value} disabled={disabled}>
|
||||
{label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
);
|
||||
};
|
||||
|
||||
export const setKOSyncSettingsWindowVisible = (visible: boolean) => {
|
||||
const dialog = document.getElementById('kosync_settings_window');
|
||||
if (dialog) {
|
||||
const event = new CustomEvent('setKOSyncSettingsVisibility', {
|
||||
detail: { visible },
|
||||
});
|
||||
dialog.dispatchEvent(event);
|
||||
}
|
||||
};
|
||||
|
||||
export const KOSyncSettingsWindow: React.FC = () => {
|
||||
const _ = useTranslation();
|
||||
const { settings, setSettings, saveSettings } = useSettingsStore();
|
||||
const { envConfig, appService } = useEnv();
|
||||
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [url, setUrl] = useState(settings.koreaderSyncServerUrl || '');
|
||||
const [username, setUsername] = useState(settings.koreaderSyncUsername || '');
|
||||
const [password, setPassword] = useState('');
|
||||
const [isConnecting, setIsConnecting] = useState(false);
|
||||
const [connectionStatus, setConnectionStatus] = useState('');
|
||||
const [deviceName, setDeviceName] = useState('');
|
||||
const [osName, setOsName] = useState('');
|
||||
|
||||
const [toleranceSliderValue, setToleranceSliderValue] = useState(() => {
|
||||
const tolerance = settings.koreaderSyncPercentageTolerance;
|
||||
return tolerance && tolerance > 0 ? Math.round(-Math.log10(tolerance)) : 4;
|
||||
});
|
||||
|
||||
// Get the OS name once
|
||||
useEffect(() => {
|
||||
const formatOsName = (name: string): string => {
|
||||
if (!name) return '';
|
||||
if (name.toLowerCase() === 'macos') return 'macOS';
|
||||
return name.charAt(0).toUpperCase() + name.slice(1);
|
||||
};
|
||||
|
||||
const getOsName = async () => {
|
||||
let name = '';
|
||||
if (appService?.appPlatform === 'tauri') {
|
||||
name = await osType();
|
||||
} else {
|
||||
const platform = getOSPlatform();
|
||||
if (platform !== 'unknown') {
|
||||
name = platform;
|
||||
}
|
||||
}
|
||||
setOsName(formatOsName(name));
|
||||
};
|
||||
getOsName();
|
||||
}, [appService]);
|
||||
|
||||
useEffect(() => {
|
||||
const defaultName = osName ? `Readest (${osName})` : 'Readest';
|
||||
setDeviceName(settings.koreaderSyncDeviceName || defaultName);
|
||||
}, [settings.koreaderSyncDeviceName, osName]);
|
||||
|
||||
const isConfigured = useMemo(
|
||||
() => !!settings.koreaderSyncUserkey,
|
||||
[settings.koreaderSyncUserkey],
|
||||
);
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const debouncedSaveDeviceName = useCallback(
|
||||
debounce((newDeviceName: string) => {
|
||||
const newSettings = { ...settings, koreaderSyncDeviceName: newDeviceName };
|
||||
setSettings(newSettings);
|
||||
saveSettings(envConfig, newSettings);
|
||||
}, 500),
|
||||
[settings, setSettings, saveSettings, envConfig],
|
||||
);
|
||||
|
||||
const handleDeviceNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const newName = e.target.value;
|
||||
setDeviceName(newName);
|
||||
debouncedSaveDeviceName(newName);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const handleCustomEvent = (event: CustomEvent) => {
|
||||
setIsOpen(event.detail.visible);
|
||||
if (event.detail.visible) {
|
||||
setUrl(settings.koreaderSyncServerUrl || '');
|
||||
setUsername(settings.koreaderSyncUsername || '');
|
||||
setPassword('');
|
||||
setConnectionStatus('');
|
||||
// Sync the slider with the current settings when opening
|
||||
const tolerance = settings.koreaderSyncPercentageTolerance;
|
||||
setToleranceSliderValue(
|
||||
tolerance && tolerance > 0 ? Math.round(-Math.log10(tolerance)) : 4,
|
||||
);
|
||||
}
|
||||
};
|
||||
const el = document.getElementById('kosync_settings_window');
|
||||
el?.addEventListener('setKOSyncSettingsVisibility', handleCustomEvent as EventListener);
|
||||
return () => {
|
||||
el?.removeEventListener('setKOSyncSettingsVisibility', handleCustomEvent as EventListener);
|
||||
};
|
||||
}, [
|
||||
settings.koreaderSyncServerUrl,
|
||||
settings.koreaderSyncUsername,
|
||||
settings.koreaderSyncPercentageTolerance,
|
||||
]);
|
||||
|
||||
const handleConnect = async () => {
|
||||
setIsConnecting(true);
|
||||
|
||||
let deviceId = settings.koreaderSyncDeviceId;
|
||||
if (!deviceId) {
|
||||
deviceId = uuidv4().replace(/-/g, '').toUpperCase();
|
||||
}
|
||||
|
||||
const client = new KOSyncClient(
|
||||
url,
|
||||
username,
|
||||
md5(password),
|
||||
settings.koreaderSyncChecksumMethod,
|
||||
deviceId,
|
||||
deviceName,
|
||||
);
|
||||
const result = await client.connect(username, password);
|
||||
|
||||
if (result.success) {
|
||||
const newSettings = {
|
||||
...settings,
|
||||
koreaderSyncServerUrl: url,
|
||||
koreaderSyncUsername: username,
|
||||
koreaderSyncUserkey: md5(password),
|
||||
koreaderSyncDeviceId: deviceId,
|
||||
koreaderSyncDeviceName: deviceName,
|
||||
koreaderSyncStrategy:
|
||||
settings.koreaderSyncStrategy === 'disabled' ? 'prompt' : settings.koreaderSyncStrategy,
|
||||
};
|
||||
setSettings(newSettings);
|
||||
await saveSettings(envConfig, newSettings);
|
||||
} else {
|
||||
setConnectionStatus('');
|
||||
eventDispatcher.dispatch('toast', {
|
||||
message: `${_('Failed to connect')}: ${_(result.message || 'Connection error')}`,
|
||||
type: 'error',
|
||||
});
|
||||
}
|
||||
setIsConnecting(false);
|
||||
setPassword('');
|
||||
};
|
||||
|
||||
const handleDisconnect = async () => {
|
||||
const newSettings = {
|
||||
...settings,
|
||||
koreaderSyncStrategy: 'disabled' as KoreaderSyncStrategy,
|
||||
koreaderSyncUserkey: '',
|
||||
};
|
||||
setSettings(newSettings);
|
||||
await saveSettings(envConfig, newSettings);
|
||||
setUsername('');
|
||||
eventDispatcher.dispatch('toast', { message: _('Disconnected'), type: 'info' });
|
||||
};
|
||||
|
||||
const handleStrategyChange = async (e: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
const newStrategy = e.target.value as KoreaderSyncStrategy;
|
||||
const newSettings = { ...settings, koreaderSyncStrategy: newStrategy };
|
||||
setSettings(newSettings);
|
||||
await saveSettings(envConfig, newSettings);
|
||||
};
|
||||
|
||||
const handleChecksumMethodChange = async (e: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
const newMethod = e.target.value as KoreaderSyncChecksumMethod;
|
||||
const newSettings = { ...settings, koreaderSyncChecksumMethod: newMethod };
|
||||
setSettings(newSettings);
|
||||
await saveSettings(envConfig, newSettings);
|
||||
};
|
||||
|
||||
const handleToleranceChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const sliderValue = parseInt(e.target.value, 10);
|
||||
setToleranceSliderValue(sliderValue);
|
||||
// Calculate the actual tolerance from the slider value (e.g., 4 -> 0.0001)
|
||||
const newTolerance = Math.pow(10, -sliderValue);
|
||||
|
||||
const newSettings = { ...settings, koreaderSyncPercentageTolerance: newTolerance };
|
||||
setSettings(newSettings);
|
||||
await saveSettings(envConfig, newSettings);
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
id='kosync_settings_window'
|
||||
isOpen={isOpen}
|
||||
onClose={() => setIsOpen(false)}
|
||||
title={_('KOReader Sync Settings')}
|
||||
boxClassName='sm:!min-w-[520px] sm:h-auto'
|
||||
>
|
||||
<div className='mb-4 mt-0 flex flex-col gap-4 p-2 sm:p-4'>
|
||||
{isConfigured ? (
|
||||
<>
|
||||
<div className='text-center'>
|
||||
<p className='text-base-content/80 text-sm'>
|
||||
{_('Sync as {{userDisplayName}}', {
|
||||
userDisplayName: settings.koreaderSyncUsername,
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
<div className='flex h-14 items-center justify-between'>
|
||||
<span className='text-base-content/80'>
|
||||
{_('Sync Server Connected', { username: settings.koreaderSyncUsername })}
|
||||
</span>
|
||||
<input
|
||||
type='checkbox'
|
||||
className='toggle'
|
||||
checked={settings.koreaderSyncStrategy !== 'disabled'}
|
||||
onChange={() => handleDisconnect()}
|
||||
/>
|
||||
</div>
|
||||
<div className='form-control w-full'>
|
||||
<label className='label py-1'>
|
||||
<span className='label-text font-medium'>{_('Sync Strategy')}</span>
|
||||
</label>
|
||||
<StyledSelect
|
||||
value={settings.koreaderSyncStrategy}
|
||||
onChange={handleStrategyChange}
|
||||
options={[
|
||||
{ value: 'prompt', label: _('Ask on conflict') },
|
||||
{ value: 'silent', label: _('Always use latest') },
|
||||
{ value: 'send', label: _('Send changes only') },
|
||||
{ value: 'receive', label: _('Receive changes only') },
|
||||
{ value: 'disable', label: _('Disabled') },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
<div className='form-control w-full'>
|
||||
<label className='label py-1'>
|
||||
<span className='label-text font-medium'>{_('Checksum Method')}</span>
|
||||
</label>
|
||||
<StyledSelect
|
||||
value={settings.koreaderSyncChecksumMethod}
|
||||
onChange={handleChecksumMethodChange}
|
||||
options={[
|
||||
{ value: 'binary', label: _('File Content (recommended)') },
|
||||
{ value: 'filename', label: _('File Name'), disabled: true },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
<div className='form-control w-full'>
|
||||
<label className='label py-1'>
|
||||
<span className='label-text font-medium'>{_('Device Name')}</span>
|
||||
</label>
|
||||
<input
|
||||
type='text'
|
||||
placeholder={osName ? `Readest (${osName})` : 'Readest'}
|
||||
className='input input-bordered h-12 w-full focus:outline-none focus:ring-0'
|
||||
value={deviceName}
|
||||
onChange={handleDeviceNameChange}
|
||||
/>
|
||||
</div>
|
||||
{/* Hidden to avoid confusing users with technical details */}
|
||||
{false && (
|
||||
<div className='form-control w-full'>
|
||||
<label className='label py-1'>
|
||||
<span className='label-text font-medium'>{_('Sync Tolerance')}</span>
|
||||
</label>
|
||||
<input
|
||||
type='range'
|
||||
min='0'
|
||||
max='15'
|
||||
value={toleranceSliderValue}
|
||||
onChange={handleToleranceChange}
|
||||
className='range range-primary'
|
||||
/>
|
||||
<div className='text-base-content/70 mt-2 text-center text-xs'>
|
||||
{_('Precision: {{precision}} digits after the decimal', {
|
||||
precision: toleranceSliderValue,
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<p className='text-base-content/70 text-center text-sm'>
|
||||
{_('Connect to your KOReader Sync server.')}
|
||||
</p>
|
||||
<div className='form-control w-full'>
|
||||
<label className='label py-1'>
|
||||
<span className='label-text font-medium'>{_('Server URL')}</span>
|
||||
</label>
|
||||
<input
|
||||
type='text'
|
||||
placeholder='https://koreader.sync.server'
|
||||
className='input input-bordered h-12 w-full focus:outline-none focus:ring-0'
|
||||
spellCheck='false'
|
||||
value={url}
|
||||
onChange={(e) => setUrl(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className='form-control w-full'>
|
||||
<label className='label py-1'>
|
||||
<span className='label-text font-medium'>{_('Username')}</span>
|
||||
</label>
|
||||
<input
|
||||
type='text'
|
||||
placeholder={_('Your Username')}
|
||||
className='input input-bordered h-12 w-full focus:outline-none focus:ring-0'
|
||||
spellCheck='false'
|
||||
value={username}
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className='form-control w-full'>
|
||||
<label className='label py-1'>
|
||||
<span className='label-text font-medium'>{_('Password')}</span>
|
||||
</label>
|
||||
<input
|
||||
type='password'
|
||||
placeholder={_('Your Password')}
|
||||
className='input input-bordered h-12 w-full focus:outline-none focus:ring-0'
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
className='btn btn-primary mt-2 h-12 min-h-12 w-full'
|
||||
onClick={handleConnect}
|
||||
disabled={isConnecting || !url || !username || !password}
|
||||
>
|
||||
{isConnecting ? <span className='loading loading-spinner'></span> : _('Connect')}
|
||||
</button>
|
||||
{connectionStatus && (
|
||||
<div className='text-error h-4 text-center text-sm'>{connectionStatus}</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
@@ -13,6 +13,7 @@ import { useThemeStore } from '@/store/themeStore';
|
||||
import { useTranslation } from '@/hooks/useTranslation';
|
||||
import { useLibraryStore } from '@/store/libraryStore';
|
||||
import { useResponsiveSize } from '@/hooks/useResponsiveSize';
|
||||
import { useSafeAreaInsets } from '@/hooks/useSafeAreaInsets';
|
||||
import { useTrafficLightStore } from '@/store/trafficLightStore';
|
||||
import { navigateToLibrary } from '@/utils/nav';
|
||||
import { debounce } from '@/utils/debounce';
|
||||
@@ -21,7 +22,7 @@ import WindowButtons from '@/components/WindowButtons';
|
||||
import Dropdown from '@/components/Dropdown';
|
||||
import SettingsMenu from './SettingsMenu';
|
||||
import ImportMenu from './ImportMenu';
|
||||
import SortMenu from './SortMenu';
|
||||
import ViewMenu from './ViewMenu';
|
||||
|
||||
interface LibraryHeaderProps {
|
||||
isSelectMode: boolean;
|
||||
@@ -44,7 +45,7 @@ const LibraryHeader: React.FC<LibraryHeaderProps> = ({
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const { appService } = useEnv();
|
||||
const { statusBarHeight } = useThemeStore();
|
||||
const { systemUIVisible, statusBarHeight } = useThemeStore();
|
||||
const { currentBookshelf } = useLibraryStore();
|
||||
const {
|
||||
isTrafficLightVisible,
|
||||
@@ -58,6 +59,7 @@ const LibraryHeader: React.FC<LibraryHeaderProps> = ({
|
||||
const headerRef = useRef<HTMLDivElement>(null);
|
||||
const iconSize18 = useResponsiveSize(18);
|
||||
const iconSize20 = useResponsiveSize(20);
|
||||
const insets = useSafeAreaInsets();
|
||||
|
||||
useShortcuts({
|
||||
onToggleSelectMode,
|
||||
@@ -102,6 +104,8 @@ const LibraryHeader: React.FC<LibraryHeaderProps> = ({
|
||||
0,
|
||||
);
|
||||
|
||||
if (!insets) return null;
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={headerRef}
|
||||
@@ -111,8 +115,8 @@ const LibraryHeader: React.FC<LibraryHeaderProps> = ({
|
||||
)}
|
||||
style={{
|
||||
marginTop: appService?.hasSafeAreaInset
|
||||
? `max(env(safe-area-inset-top), ${statusBarHeight}px)`
|
||||
: '',
|
||||
? `max(${insets.top}px, ${systemUIVisible ? statusBarHeight : 0}px)`
|
||||
: '0px',
|
||||
}}
|
||||
>
|
||||
<div className='flex w-full items-center justify-between space-x-6 sm:space-x-12'>
|
||||
@@ -224,7 +228,7 @@ const LibraryHeader: React.FC<LibraryHeaderProps> = ({
|
||||
buttonClassName='btn btn-ghost h-8 min-h-8 w-8 p-0'
|
||||
toggleButton={<PiDotsThreeCircle size={iconSize18} />}
|
||||
>
|
||||
<SortMenu />
|
||||
<ViewMenu />
|
||||
</Dropdown>
|
||||
<Dropdown
|
||||
className='exclude-title-bar-mousedown dropdown-bottom dropdown-end'
|
||||
|
||||
@@ -8,6 +8,7 @@ import { TbSunMoon } from 'react-icons/tb';
|
||||
import { BiMoon, BiSun } from 'react-icons/bi';
|
||||
|
||||
import { setAboutDialogVisible } from '@/components/AboutWindow';
|
||||
import { setKOSyncSettingsWindowVisible } from './KOSyncSettings';
|
||||
import { isTauriAppPlatform, isWebAppPlatform } from '@/services/environment';
|
||||
import { DOWNLOAD_READEST_URL } from '@/services/constants';
|
||||
import { useAuth } from '@/context/AuthContext';
|
||||
@@ -33,11 +34,13 @@ const SettingsMenu: React.FC<SettingsMenuProps> = ({ setIsDropdownOpen }) => {
|
||||
const router = useRouter();
|
||||
const { envConfig, appService } = useEnv();
|
||||
const { user } = useAuth();
|
||||
const { userPlan, quotas } = useQuotaStats(true);
|
||||
const { themeMode, setThemeMode } = useThemeStore();
|
||||
const { settings, setSettings, saveSettings } = useSettingsStore();
|
||||
const [isAutoUpload, setIsAutoUpload] = useState(settings.autoUpload);
|
||||
const [isAutoCheckUpdates, setIsAutoCheckUpdates] = useState(settings.autoCheckUpdates);
|
||||
const [isAlwaysOnTop, setIsAlwaysOnTop] = useState(settings.alwaysOnTop);
|
||||
const [isAlwaysShowStatusBar, setIsAlwaysShowStatusBar] = useState(settings.alwaysShowStatusBar);
|
||||
const [isScreenWakeLock, setIsScreenWakeLock] = useState(settings.screenWakeLock);
|
||||
const [isOpenLastBooks, setIsOpenLastBooks] = useState(settings.openLastBooks);
|
||||
const [isAutoImportBooksOnOpen, setIsAutoImportBooksOnOpen] = useState(
|
||||
@@ -46,8 +49,6 @@ const SettingsMenu: React.FC<SettingsMenuProps> = ({ setIsDropdownOpen }) => {
|
||||
const [isTelemetryEnabled, setIsTelemetryEnabled] = useState(settings.telemetryEnabled);
|
||||
const iconSize = useResponsiveSize(16);
|
||||
|
||||
const { quotas } = useQuotaStats();
|
||||
|
||||
const showAboutReadest = () => {
|
||||
setAboutDialogVisible(true);
|
||||
setIsDropdownOpen?.(false);
|
||||
@@ -83,6 +84,13 @@ const SettingsMenu: React.FC<SettingsMenuProps> = ({ setIsDropdownOpen }) => {
|
||||
setIsDropdownOpen?.(false);
|
||||
};
|
||||
|
||||
const toggleOpenInNewWindow = () => {
|
||||
settings.openBookInNewWindow = !settings.openBookInNewWindow;
|
||||
setSettings(settings);
|
||||
saveSettings(envConfig, settings);
|
||||
setIsDropdownOpen?.(false);
|
||||
};
|
||||
|
||||
const toggleAlwaysOnTop = () => {
|
||||
settings.alwaysOnTop = !settings.alwaysOnTop;
|
||||
setSettings(settings);
|
||||
@@ -92,6 +100,14 @@ const SettingsMenu: React.FC<SettingsMenuProps> = ({ setIsDropdownOpen }) => {
|
||||
setIsDropdownOpen?.(false);
|
||||
};
|
||||
|
||||
const toggleAlwaysShowStatusBar = () => {
|
||||
settings.alwaysShowStatusBar = !settings.alwaysShowStatusBar;
|
||||
setSettings(settings);
|
||||
saveSettings(envConfig, settings);
|
||||
setIsAlwaysShowStatusBar(settings.alwaysShowStatusBar);
|
||||
setIsDropdownOpen?.(false);
|
||||
};
|
||||
|
||||
const toggleAutoUploadBooks = () => {
|
||||
settings.autoUpload = !settings.autoUpload;
|
||||
setSettings(settings);
|
||||
@@ -143,6 +159,16 @@ const SettingsMenu: React.FC<SettingsMenuProps> = ({ setIsDropdownOpen }) => {
|
||||
setIsTelemetryEnabled(settings.telemetryEnabled);
|
||||
};
|
||||
|
||||
const showKoSyncSettingsWindow = () => {
|
||||
setKOSyncSettingsWindowVisible(true);
|
||||
setIsDropdownOpen?.(false);
|
||||
};
|
||||
|
||||
const handleUpgrade = () => {
|
||||
navigateToProfile(router);
|
||||
setIsDropdownOpen?.(false);
|
||||
};
|
||||
|
||||
const avatarUrl = user?.user_metadata?.['picture'] || user?.user_metadata?.['avatar_url'];
|
||||
const userFullName = user?.user_metadata?.['full_name'];
|
||||
const userDisplayName = userFullName ? userFullName.split(' ')[0] : null;
|
||||
@@ -172,7 +198,9 @@ const SettingsMenu: React.FC<SettingsMenuProps> = ({ setIsDropdownOpen }) => {
|
||||
}
|
||||
>
|
||||
<ul>
|
||||
<Quota quotas={quotas} labelClassName='h-10 pl-3 pr-2' />
|
||||
<div onClick={handleUserProfile} className='cursor-pointer'>
|
||||
<Quota quotas={quotas} labelClassName='h-10 pl-3 pr-2' />
|
||||
</div>
|
||||
<MenuItem label={_('Account')} noIcon onClick={handleUserProfile} />
|
||||
</ul>
|
||||
</MenuItem>
|
||||
@@ -206,6 +234,13 @@ const SettingsMenu: React.FC<SettingsMenuProps> = ({ setIsDropdownOpen }) => {
|
||||
/>
|
||||
)}
|
||||
<hr className='border-base-200 my-1' />
|
||||
{appService?.hasWindow && (
|
||||
<MenuItem
|
||||
label={_('Open Book in New Window')}
|
||||
Icon={settings.openBookInNewWindow ? MdCheck : undefined}
|
||||
onClick={toggleOpenInNewWindow}
|
||||
/>
|
||||
)}
|
||||
{appService?.hasWindow && <MenuItem label={_('Fullscreen')} onClick={handleFullScreen} />}
|
||||
{appService?.hasWindow && (
|
||||
<MenuItem
|
||||
@@ -214,6 +249,13 @@ const SettingsMenu: React.FC<SettingsMenuProps> = ({ setIsDropdownOpen }) => {
|
||||
onClick={toggleAlwaysOnTop}
|
||||
/>
|
||||
)}
|
||||
{appService?.isMobileApp && (
|
||||
<MenuItem
|
||||
label={_('Always Show Status Bar')}
|
||||
Icon={isAlwaysShowStatusBar ? MdCheck : undefined}
|
||||
onClick={toggleAlwaysShowStatusBar}
|
||||
/>
|
||||
)}
|
||||
<MenuItem
|
||||
label={_('Keep Screen Awake')}
|
||||
Icon={isScreenWakeLock ? MdCheck : undefined}
|
||||
@@ -232,6 +274,11 @@ const SettingsMenu: React.FC<SettingsMenuProps> = ({ setIsDropdownOpen }) => {
|
||||
onClick={cycleThemeMode}
|
||||
/>
|
||||
<hr className='border-base-200 my-1' />
|
||||
<MenuItem label={_('KOReader Sync')} onClick={showKoSyncSettingsWindow} />
|
||||
<hr className='border-base-200 my-1' />
|
||||
{user && userPlan === 'free' && !appService?.isIOSApp && (
|
||||
<MenuItem label={_('Upgrade to Readest Premium')} onClick={handleUpgrade} />
|
||||
)}
|
||||
{isWebAppPlatform() && <MenuItem label={_('Download Readest')} onClick={downloadReadest} />}
|
||||
<MenuItem label={_('About Readest')} onClick={showAboutReadest} />
|
||||
<MenuItem
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user