Compare commits

..

663 Commits

Author SHA1 Message Date
Huang Xin 56d6aceb0d release: version 0.11.1 (#4123) 2026-05-11 06:18:03 +02:00
Huang Xin 598eb77237 feat(library): redesign empty-library onboarding (#4122)
First-run users opening Readest with no books now see a typographic
hero instead of the previous generic "Welcome to your library" hero.

Key UX changes:
- 64px PiBooks glyph at base-content/60 anchors a single-column
  composition (max-w-md container, max-w-xs button stack)
- Headline "Start your library" — action-led, not "Welcome to X"
- Platform-aware description:
    desktop: "Drop a book anywhere on this window, or pick one from
             your computer."
    mobile : "Pick a book from your device to add it to your library."
  Branched on appService.isMobile so the touch-only flows don't see
  drag-and-drop language.
- Auth-aware secondary action: a quiet underlined "Sign in to sync
  your library" text link renders only when logged out; signed-in
  users get just the Import CTA (sync runs automatically).
- Primary CTA "Import Books" unchanged; routes to existing file
  picker. The surrounding hero drop-zone wrapper is preserved so
  drag-and-drop import keeps working on desktop.
- TODO marker for a future "Browse free catalogs" entry above the
  secondary action slot.

Implementation:
- Extracted as src/app/library/components/LibraryEmptyState.tsx
  (~60 lines, single onImport prop) so the empty branch can be
  unit-tested without mounting the full LibraryPageContent.
- src/app/library/page.tsx swaps ~17 lines of inline hero JSX for
  one <LibraryEmptyState onImport={handleImportBooksFromFiles} />.
- Four unit tests cover desktop render, mobile render, auth-aware
  sync-button hide, and import-click callback.

i18n: four new strings translated across 33 locales; en/translation.json
untouched per the project convention (non-plural strings live in
code). Stale "Welcome to your library..." key removed by the scanner.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 05:58:54 +02:00
Huang Xin d326e1c73d fix: hide popup triangle when inside popup + EPUB image-only paragraph rendering (#4121)
- Popup: hide the inner triangle when its anchor point lands inside
  the popup body. Extracted as a generic `isPointInRect` helper in
  `sel.ts` (with a default 1px padding so edge cases stay visible).
- style.ts: handle `<p[width][height]><img></p>` (common in some
  MOBI conversions) — clear hardcoded width/height and apply
  multiply blend for dark themes so the image doesn't sit on a
  colored box.
- Annotator: shrink dict popup height from 480 to 360 to fit
  smaller screens.
- foliate-js: submodule bump.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 04:12:37 +02:00
Huang Xin 1705006b6b fix(mobile): iOS PIN keyboard UX + Safari font line-height in EPUBs (#4120)
- AppLockScreen: pad the lock screen bottom by the on-screen
  keyboard height tracked via visualViewport, so the flex-centered
  PIN sits above the keyboard on iOS WKWebView where dvh does not
  shrink.
- AppLockScreen: skip stickyFocus on mobile. iOS will not pop the
  keyboard from a programmatic .focus(), so the cursor would blink
  with no input — wait for the user's tap instead.
- PinInput: forward autoFocus to the input when autoFocus or
  stickyFocus is set, for more reliable mount-time focus.
- style.ts: give legacy <p><font>...</font></p> its own block
  context so iOS Safari applies the inherited line-height.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 20:42:42 +02:00
Huang Xin 952e436514 i18n: update translations (#4118) 2026-05-10 18:28:56 +02:00
Huang Xin 772bb73b46 ui/ux: codify design system and migrate settings to shared primitives (#4116)
* ui/ux: codify design system and migrate settings to shared primitives

Document Readest's design language in DESIGN.md (Adwaita-aligned, e-ink-first,
RTL-correct) and migrate every settings panel onto a small set of primitives
(BoxedList, SettingsRow, SettingsSwitchRow, SettingsSelect, SettingsInput,
NavigationRow, Tips, SubPageHeader). AGENTS.md links to DESIGN.md so contributors
land there before inventing new chassis classes.

Replace the standalone KOReader/Readwise/Hardcover Config dialogs with a single
Integrations panel (Reading Sync + Content Sources sub-pages). The reader's
BookMenu now hides each provider until it's configured, and Hardcover's per-book
"Enable for This Book" toggle is dropped — there's no auto-sync to gate, so the
flag was just extra clicks.

Refresh highlight colors (two-trigger swatch + label, translatable default
names), background texture / theme color selectors (border-current keeps
selection legible on any backdrop), CustomFonts/CustomDictionaries (quiet
list-extension style + shared Tips primitive), the OPDS catalog manager
(debounced auto-download, right-aligned Browse), Set PIN, and the KOSync
conflict resolver. Translate the ~30 new strings across all 33 locales.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* ui/ux: responsive typography, OPDS card polish, deep-link return paths

Restore the .settings-content responsive cascade (14px desktop / 16px
mobile) the legacy panels relied on by dropping hardcoded `text-sm`/`text-xs`
from the new primitives. Secondary text moves to em-relative `text-[0.85em]`
so it scales with the parent. Form controls (`<input>`, `<select>`) re-apply
the cascade explicitly via the `settings-content` class since browsers don't
inherit font-size onto form elements.

Extract `<SectionTitle>` primitive (caseless-language aware via
`isCaselessUILang`/`isCaselessLang`) and route every uppercase tag-style
header through it: BoxedList groups, Reading Sync, Content Sources, Theme
Color, Background Image, integration form labels, KOSyncResolver device
labels, and the OPDS My Catalogs / Popular Catalogs sections. CJK / Arabic
/ Hebrew / Indic / Thai / Tibetan locales bump to `1em` since `uppercase`
is a no-op on those scripts.

Redesign the OPDS My Catalogs cards: whole card becomes the browse trigger
(role='button'), edit/delete collapse into a 3-dot dropdown menu, and the
sync-status moves to a sub-line under Auto-download so the card height stays
constant whether the toggle is on/off or sync data has arrived.

Plumb a `from=settings-integrations` URL marker through the OPDS browser so
both manual close and auto-close-on-failure (preserved as `router.back()`
for transient failures, paired with a new `stashOPDSReturnTarget` helper)
return the user to Settings -> Integrations -> OPDS Catalogs sub-page
rather than the dialog's top level. Backed by new `requestedSubPage`
deep-link store field.

Skip the OPDS catalog passphrase prompt when credentials sync is disabled
-- `replicaPublish` already drops encrypted fields at the wire, so prompting
was both pointless and confusing.

Fix `SettingsDialog` calling `setRequestedPanel(null)` inside a `useState`
lazy initializer (zustand setter during render -> React warning); move the
clear into a one-shot `useEffect`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* ui/ux: opt Settings into OverlayScrollbars + caseless typography polish

Add an opt-in `useOverlayScroll` prop to `<Dialog>` that swaps the body's
native `overflow-y-auto` for `<OverlayScrollbarsComponent>` (autohide,
click-scroll, no native overlaid bars). SettingsDialog flips it on so the
long Layout / Color panels keep a visible, theme-aware scroll track on
Android / iOS webviews where native scrollbars auto-hide entirely. Other
short-modal callers stay on the native scrollbar.

Drop the `uppercase tracking-wider` SectionTitle styling for caseless
scripts and pair it with body-weight `font-medium` instead — those
typographic effects are no-ops on Han / Hangul / Devanagari / Thai etc.,
so a plain medium-weight body-size title reads more correctly than a
shrunken pseudo-uppercase one. SettingsRow / NavigationRow primary labels
follow the same rule (drop `font-medium` in caseless locales since the
inherited body weight already carries; CJK fonts bold poorly at body
size).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* ui/ux: SettingLabel primitive + KOSyncForm select polish + Tips alignment

Add `<SettingLabel>` primitive — caseless-aware row/field label that pairs
with `<SectionTitle>` (groups) for per-item labels. Cased scripts get
`font-medium`; caseless scripts (CJK / Arabic / Hebrew / Indic / Thai /
Tibetan) drop the weight since Han / Hangul / Devanagari etc. bold poorly
at body size. No font-size class so it inherits the `.settings-content`
14/16 cascade. Routed through `SettingsRow`, `NavigationRow`, and the
~12 ad-hoc inline `text-sm font-medium` callsites in AIPanel / FontPanel
/ ColorPanel / IntegrationsPanel / KOSync / Readwise / Hardcover forms.

Refactor KOSyncForm's Sync Strategy + Checksum Method rows onto the
shared `<SettingsSelect>` primitive — the inline 17-line div/select/
MdArrowDropDown chassis becomes a single SettingsSelect call with an
options array. Drops the unused MdArrowDropDown import and ~25 lines.

Fix Tips list-item alignment: callers traditionally pass `<li>` elements
(semantic) but the primitive was double-wrapping into `<li><span><li>...</li></span></li>` — invalid HTML, and the inner `<li>`'s
`display: list-item` broke line-wrap alignment on multi-line items.
Unwrap caller `<li>` to its content; add `flex-1` on the text span so
wrapped lines align under the first line instead of falling back to the
bullet column. Bullet container switches to `h-[1.4em]` so it tracks the
text line-height and pins to the first line's optical center via
`items-center` regardless of how much the content wraps.

DESIGN.md §5 typography updated to point primary-label callers at
`<SettingLabel>`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 17:46:25 +02:00
Huang Xin 5774e00c09 feat(sync): opt-in Credentials toggle + keyring v4 migration (#4111)
* feat(sync): add opt-in Credentials toggle to Manage Sync

Adds a new Credentials category (default OFF) that gates the encrypted
fields (OPDS / KOSync / Readwise / Hardcover usernames, passwords, and
tokens) at both the publish and pull pipelines. When off, sensitive
fields never leave the device, the proactive passphrase prompt never
fires, and the Sync passphrase panel is hidden entirely.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* security: bump keyring to version 4

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 19:09:57 +02:00
Huang Xin f6f446e8a0 feat(applock): blinking PIN cursor + misc UI polish (#4110)
* feat(applock): show blinking cursor on PIN input

Empty PIN slots used to render nothing, leaving no cue for which
position is active. Add a thin underscore that blinks under the
next-to-fill slot while the input is focused.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore(ui): misc settings polish + i18n refresh

- applock screen: switch fixed positioning to full-height so the lock
  screen sits inside the safe area
- applock dialog: split the recovery sentence so it reads cleanly
  without an em dash
- settings: rename "Interface Language" to "Language"
- translators: drop the "(Unavailable)" suffix from disabled providers;
  the row already greys out
- ruler color picker: keep swatches clickable when ruler is off so
  users can still set a color before enabling
- refresh translations across all locales for the changed strings

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 16:58:59 +02:00
Huang Xin 1eae2af23e feat(sync): batch replica sync into one /api/sync/replicas request (#4109)
Auto-sync triggers (boot non-settings, focus, visibilitychange, online,
periodic) used to fan out N parallel `GET /api/sync/replicas?kind=…`
requests, one per replica kind. With 5 kinds today and the focus path
firing on every foreground transition, that's 5x the Cloudflare Worker
invocations of what the work actually requires.

Server: extend POST /api/sync/replicas to accept a batched-pull body
(`{ cursors: [{kind, since}, …] }`) alongside the existing push
(`{ rows: […] }`). Per-kind queries fan out via Promise.all — Supabase
calls inside the Worker aren't billed as Cloudflare requests, so DB
load is unchanged while Worker invocations collapse from N to 1.

Client/manager: add `client.pullBatch` and `manager.pullMany` that
share the existing cursor/HLC machinery. The boot path's `since=null`
override carries over via `pullMany(kinds, { since: null })`.

Orchestrator: `triggerIncrementalPullAll` now does ONE pullMany call
then fans out per-kind apply via Promise.allSettled. Boot does the
same for non-settings kinds (settings stays a single call to preserve
its apply-first ordering invariant).

Foreground triggers: listen to BOTH `focus` and `visibilitychange`,
sharing one throttle. focus is fastest on iOS Tauri WKWebView (~T=0,
~400ms ahead of visibilitychange). visibilitychange is the only
signal that fires on browser tab switching — focus does not. Drops
the Supabase user-ref-change listener (was the slowest of the three
foreground signals; redundant with the DOM events).

Bonus: `useBooksSync` now serializes `handleAutoSync` against
`pullLibrary` via the shared `isPullingRef` gate. The two paths used
to fire two concurrent `/api/sync?type=books` requests on the same
`since` value at startup; now whichever runs first claims the gate
and the other skips (throttle's `emitLast` retries afterwards).

Per session: boot 5→2 Worker calls. Per foreground trigger: 5→1.
2026-05-09 16:18:46 +02:00
Huang Xin 295a588988 feat(share): route annotation exports through the system share sheet (#4107)
Adds a `share` flag and `sharePosition` to `saveFile` across the app
services. On iOS/Android/macOS/Windows the annotation export now calls
the sharekit `shareFile` (writing the markdown/txt to `$TEMP` first when
no `filePath` is provided), so users get the system "Share via…" sheet
that drops the export into Mail, Notes, Messages, etc. Linux desktop
keeps the existing save dialog, since sharekit has no Linux backend.

On the web, `saveFile` now prefers `navigator.share({ files })` when the
browser advertises support via `canShare`. AbortError (user dismissed)
is treated as a deliberate "don't share" choice; any other rejection
(e.g., Chrome desktop's `NotAllowedError` despite a positive `canShare`)
falls through to the `<a download>` fallback so a save still happens.

Also fixes the macOS share popover anchoring: `preferredEdge: 'top'`
maps to `NSMaxYEdge`, which is the rect's bottom edge in WKWebView's
flipped coords, so the picker rendered below the trigger button. The
annotations export only got away with it because its dialog has no room
below — macOS auto-flipped above. Switching to `preferredEdge: 'bottom'`
(`NSMinYEdge` → top edge in flipped coords) anchors the popover above
the button consistently. Adds `$TEMP/**/*` to the Tauri fs capabilities
so the writable temp share file is permitted.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 13:50:08 +02:00
Huang Xin 4110911011 fix(sync): keep dictionarySettings consistent across devices (#4105)
The bundled `settings` replica's `dictionarySettings.providerOrder`
and `providerEnabled` repeatedly drifted on multi-device setups: a
fresh-install Device B would overwrite Device A's authoritative
order with its own local default, dict tombstones referenced via
the settings replica left "skipped" gaps in the UI, and providerEnabled
keys missing from providerOrder rendered as silently lost imports.

Six related fixes (mostly orthogonal):

- **Disk-priming** in `initSettingsSync(initialSettings)`: seeds
  `lastPublishedFields` from the just-loaded disk settings so the
  first `setSettings(disk_default)` at boot diffs against the disk
  baseline (no diff → no push), instead of diffing every whitelisted
  field against `undefined` and clobbering the server with locals.
- **Settings boot pull is awaited first** in `useReplicaPull` (with
  a shared `settingsBootPullPromise`) so the dict/font/texture/opds
  pulls' auto-saves see server-primed `lastPublishedFields` rather
  than disk defaults — implicit even when the caller didn't request
  the `settings` kind.
- **Visibility / online / periodic auto-pull** in `useReplicaPull`:
  module-level listeners with a 30s visibility throttle and a 5-min
  interval keep long-lived foreground tabs in sync (previously the
  hook only did the once-per-session boot pull and `ReplicaSyncManager.startAutoSync`'s
  comments lied — it only flushed dirty pushes).
- **Tombstone scrubbing for no-local rows**: `softDeleteByContentId`
  scrubs `providerOrder` / `providerEnabled` by contentId regardless
  of whether a local dict matches, and `applyRow` always invokes it
  on tombstones — so Device B fresh-installs that pulled tombstoned
  contentIds via the settings replica without ever having a local row
  still get the provider-side entries cleaned.
- **Orphan rescue** in `loadCustomDictionaries`: providerEnabled keys
  that have no slot in providerOrder (per-field LWW splits a settings
  push) get spliced before the first builtin so user-imported dicts
  stay contiguous near the top of the list, not stranded after the
  builtins where users miss them.
- **`addDictionary` prepends** to `providerOrder` so a fresh local
  import shows up at the top of the list. Reviving a soft-deleted
  entry preserves its existing slot.
- **Explicit-publish gate for `providerOrder`**: `markExplicitProviderOrderPublish()`
  in `replicaSettingsSync` is the only way for `publishSettingsIfChanged`
  to ship `dictionarySettings.providerOrder`. UI handlers that
  intentionally reorder (drag-drop, dict import, dict delete,
  web-search add) opt in via `saveCustomDictionaries(env, { publishOrderChange: true })`.
  Auto-mutations from replica pull / orphan-rescue / tombstone-scrub
  no longer ever republish the local view of order.

12 new tests across `replicaSettingsSync`, `replicaPullAndApply`,
`useReplicaPull`, and `customDictionaryStore`.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 10:40:19 +02:00
Huang Xin e0b3a6fb0c fix(i18n): localize quota reset countdown time units (#4104)
The "Resets in {{duration}}" indicator on the user page formatted its
duration via dayjs with literal "[hr]" / "[min]" tokens, which bypassed
i18n entirely. Non-English locales rendered mixed strings like
"18 hr 6 min后重置".

Move the unit literals into the translation key itself
("Resets in {{hours}} hr {{minutes}} min") so each locale controls word
order and unit abbreviations, and translate the new key for all 33
shipped locales.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 09:12:53 +02:00
Huang Xin c30a59a9ed fix(epub): accept EPUBs with malformed first ZIP local file header (#4103)
Some EPUB writers (e.g., "ebookredo") emit a non-standard local file
header signature on the first entry — bytes like PK\x03\x02 instead of
PK\x03\x04. The archive is still readable via the End of Central
Directory record, and @zip.js/zip.js handles it without complaint, but
DocumentLoader.isZip() rejected the file at the magic-bytes gate before
zip.js ever ran. The user saw "Unsupported or corrupted book file" on
a perfectly readable EPUB.

Drop the strict 4th-byte equality check. PK\x03 alone identifies a
local file header — no other ZIP record signature starts that way — so
loosening the check is safe and aligns with what tolerant ZIP parsers
already do.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 07:37:28 +02:00
Huang Xin ae42dcb53a fix(txt): parse author from txt filename and use edited metadata on fallback cover (#4095) (#4102)
When importing a `.txt` file the author field stayed empty unless the
text content itself contained an `作者:…` header, even when the filename
already encoded it. Common Chinese naming patterns like `《书名》作者:张三.txt`,
`《书名》[张三].txt`, or `《书名》张三.txt` now contribute the author when
the file body doesn't.

- Added `extractTxtFilenameMetadata` in `utils/txt.ts` and replaced the
  ad-hoc `extractBookTitle` regex used by both convertSmallFile and
  convertLargeFile. Content-extracted author still wins; the filename
  author is the next fallback before the caller-provided one.
- `BookCover` now reads `book.author || book.metadata?.author` so the
  author typed into the metadata edit dialog shows on auto-generated
  fallback covers when the original `book.author` was empty.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 05:34:56 +02:00
Huang Xin 302363a9fd feat(sync): per-category sync gates + Manage Sync UI (#4099)
* feat(sync): add per-category sync gates + Manage Sync UI

The user can now enable / disable each sync category independently
in Settings → Data Sync (User page). The map syncs across devices
via the bundled `settings` replica, defaults to enabled so the
preference is opt-out, and applies on both push (`replicaPublish`,
legacy `useSync`) and pull (`useReplicaPull`, `useSync`) without
backfilling on re-enable.

Categories:
- `book` / `progress` / `note` — gate the legacy `SyncClient` paths
- `dictionary` / `font` / `texture` / `opds_catalog` — gate the
  replica-sync pulls + publishes for those kinds
- `settings` — togglable, but force-on while `dictionary` is enabled
  because the dictionary's `providerOrder` / `providerEnabled` /
  `webSearches` live in the bundled settings replica. The UI
  shows the locked toggle as blue (enabled) with a hint instead of
  greying it out, since the underlying state IS on.

UI:
- New `SyncCategoriesSection` lists every category with a
  description and a daisyUI toggle.
- New `Manage Sync` blue action on the User page (second slot,
  right after `Manage Subscription`); also surfaces inside the
  library `Advanced Settings` menu, deep-linking via
  `/user?section=sync`.
- `SyncPassphraseSection` moved into the Manage Sync panel
  alongside the categories list. `Unlock now` button removed —
  the gate fires automatically on first encrypted push/pull and
  the manual unlock affordance was confusing.

Adjacent cleanups:
- `LangPanel` Dictionaries card gets `overflow-hidden` so the
  hover highlight clips to the card's rounded corners.
- `FontPanel` gear icon replaced with a `Manage Fonts` row that
  matches the `Manage Dictionaries` pattern.

i18n: extracted + translated 31 in-scope locales for the new
strings (`Manage Sync`, `Data Sync`, `Manage Fonts`, plus the
category copy block).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore(i18n): translate new sync-categories strings across 30 locales

Adds translations for the four strings extracted after the latest
SyncCategoriesSection iteration:

- `App settings` — toggle label for the bundled-settings sync gate
- `Theme, highlight colours, integrations (KOSync, Readwise,
  Hardcover), and dictionary order` — description under that toggle
- `Required while Dictionaries sync is enabled` — hint shown when
  the toggle is locked because dictionary sync depends on settings
- `Unavailable` — `(Unavailable)` suffix on disabled translator
  providers; was missing from most locales until i18next-scanner
  picked it up this run

Product names (KOSync, Readwise, Hardcover) left in Latin script.
`Dictionaries` references in the third string reuse each locale's
existing translation. `pt-BR` and `uz` deliberately untouched (out
of the in-scope set).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore(i18n): fill in pt-BR for the new sync-categories strings

`pt-BR` is a registered, shipped locale (Portuguese (Brasil)) that
fell through the gaps in earlier batch runs because it isn't listed
in the i18n skill's locale-reference table. The fallback chain
`pt-BR → pt → en` softened the impact, but BR-specific phrasing
needs its own translations for the 20 new keys this PR added.

`uz` stays excluded — that locale isn't registered anywhere
(missing from i18next-scanner.config.cjs, src/i18n/i18n.ts, and
TRANSLATED_LANGS), so its translation file is dead code.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore(i18n): translate uz for the new sync-categories strings

`uz` is a registered locale (listed in `i18n-langs.json` and
`TRANSLATED_LANGS` as `'Oʻzbek'`) but earlier batch translation
runs excluded it because the i18n skill's static locale-reference
table was incomplete. Filling in the 20 strings this PR added.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore(agent): update i18n skill

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 22:52:09 +02:00
Huang Xin cc8f917cdd fix(layout): silence viewport meta warning on non-Android browsers (#4097)
`interactive-widget=resizes-content` was set in the SSR viewport
metadata so Android Chrome would shrink the layout viewport when
the on-screen keyboard opens (matching iOS default behavior).
Other browsers — Safari on macOS / iOS, desktop Chrome, Firefox —
log a console warning every page load because they don't recognize
the key.

Move the attachment client-side, gated on a UA sniff for Android,
so the meta tag stays clean for everyone else. The Android-specific
behavior (modals centered above the keyboard) is preserved on the
platform that actually needed it.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 21:16:52 +02:00
Huang Xin 51a553dd89 feat(sync): bundle dictionary settings into the settings replica kind (#4096)
* feat(sync): bundle dictionary settings into the `settings` replica kind

Adds three entries to the SETTINGS_WHITELIST so `providerOrder`,
`providerEnabled`, and `webSearches` flow through the bundled
settings replica with whole-field LWW. `defaultProviderId` (last-
used tab) is deliberately excluded — it's per-device state.

The customDictionaryStore exposes `applyRemoteDictionarySettings`
so pulled values propagate into the in-memory mirror that the
reader popup and the dictionary settings panel read from. Without
this the mirror would stay stale until the next panel mount.

Also fixes `saveCustomDictionaries`: it mutated the existing
settings object in place and called `setSettings` with the same
reference, so the replicaSettingsSync subscriber saw no change
and never published. Build a fresh settings reference instead.

Collapses the original PR 6 (`dict_provider_position`) and PR 7
(`dict_web_search`) plans, which proposed per-element CRDT rows
with deterministic actor-id tiebreaks. Whole-field LWW is the
right call given how rarely users edit these on two devices at
once — same precedent as `customHighlightColors` and
`customThemes` shipping through the bundled kind in PR 5.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(sync): make dict.id stable across devices (= contentId)

Replaces the per-device `Math.random()` bundleDir as `dict.id` with
the cross-device-stable `contentId`. `bundleDir` keeps tracking the
device-local on-disk path, so on-disk layout is unchanged.

Touch points:
- 4 import paths in `dictionaryService.ts` + `buildLocalDictFromRow`
  in `replicaDictionaryApply.ts` set `id: contentId` instead of
  `id: bundleDir`.
- Every `providerOrder` / `providerEnabled` entry that came from
  the dict store is now uniformly contentId-keyed, so the bundled
  `settings` replica syncs them across devices without any seam
  translation.

Dicts with no contentId (very old, never synced) keep their
bundleDir as id and remain local-only.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 20:42:10 +02:00
Huang Xin 6e7c9d1395 feat(sync): bundled settings replica kind for cross-device prefs and credentials (#4094)
* feat(sync): add bundled `settings` replica kind for cross-device prefs and credentials

Adds a single-row `settings` replica that syncs a whitelist of
`SystemSettings` fields across devices via per-field LWW (one entry
per dot-namespaced path). Plaintext for theme / highlight colour /
TTS configuration; encrypted (AES-GCM under the user's sync
passphrase) for kosync / Readwise / Hardcover credentials.

Highlights:
- Push-side diff against an in-memory snapshot for plaintext paths
  and a localStorage SHA-256 hash for encrypted paths, so a refresh
  doesn't re-publish or re-prompt for the passphrase.
- Pull-side cipher-fingerprint dedupe + per-row passphrase gate;
  decryption failures surface as toasts (wrong passphrase / orphan
  cipher) instead of silent drops.
- Auto-recovery for orphaned ciphers: when a row references a
  saltId no longer in `replica_keys`, clear the local hash and
  re-encrypt under the current salt on the next save.
- Single in-flight `/sync/replica-keys` fetch with a value cache
  to coalesce the boot-time burst of concurrent unlock callers.

* fix(sync): guard settings dot-path helpers against prototype-polluting keys

Reject `__proto__`, `constructor`, and `prototype` segments in the
settings adapter's `readPath` / `writePath`. Every caller currently
passes a constant from `SETTINGS_WHITELIST`, so the guard is purely
defensive — but it silences the CodeQL prototype-pollution warning
on PR #4094 and keeps the helpers safe if a future call site ever
forwards an untrusted path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 19:03:23 +02:00
Huang Xin 2d5590ec1f feat(applock): 4-digit PIN gate at app launch (#4093)
Closes #2285.

Adds an opt-in 4-digit PIN that gates the library and reader on app
launch. Threat model: casual physical/browser access by another person
on a shared device — peace of mind, not defense against an attacker
with filesystem access. The PIN is stored as a salted PBKDF2-SHA256
hash (100k iterations) in settings.json; the plaintext PIN is never
persisted.

Configured from Settings → Advanced Settings → "Set PIN…" (and
"Change PIN…" / "Disable PIN…" once enabled). The lock screen and the
set/change/disable dialog share a single 4-dot input component
(PinInput) for a consistent UI; the dialog auto-advances focus from
Current → New → Confirm. Lock-on-resume, biometric unlock, and
account-based reset are out of scope for this MVP — disable for now is
"clear app data".

Bundles the previously-missed sync-passphrase i18n strings (PR #4090)
across all 33 locales so no `__STRING_NOT_TRANSLATED__` placeholders
remain in the tree.

New
- src/libs/crypto/applock.ts (PBKDF2 hash/verify; reuses derivePbkdf2Key)
- src/store/appLockStore.ts (gate + dialog state)
- src/components/PinInput.tsx (shared 4-dot input)
- src/components/AppLockScreen.tsx (full-screen lock gate)
- src/components/settings/AppLockDialog.tsx (set/change/disable)
- src/__tests__/libs/crypto/applock.test.ts

Modified
- src/types/settings.ts (pinCodeEnabled / pinCodeHash / pinCodeSalt)
- src/services/constants.ts (default off)
- src/components/Providers.tsx (mount gate + dialog above app shell)
- src/app/library/components/SettingsMenu.tsx (Advanced submenu entries)
- src/styles/globals.css (animate-pin-shake keyframe)
- public/locales/*/translation.json (21 PIN keys + 17 leftover passphrase keys × 33 locales)

Verified
- pnpm test (4018 pass)
- pnpm lint (clean)
- Manual web smoke: Set/Reload-locks/Wrong-PIN/Unlock/Change/Disable

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 18:10:34 +02:00
Huang Xin dc58d985e7 fix(layout): center fixed modals above the on-screen keyboard on Android (#4091)
Adds `interactive-widget=resizes-content` to the viewport meta. iOS
Safari already shrinks the layout viewport when the keyboard opens,
so existing `fixed inset-0` flex-centered modals (PassphrasePrompt,
GroupingModal, etc.) auto-center in the visible space. Android
Chrome defaults to `resizes-visual` — only the visual viewport
shrinks, layout stays full-height — leaving those modals rendered
under the keyboard. Switching to `resizes-content` makes Android
match iOS without any per-modal JS.

Updates both the App Router viewport export (src/app/layout.tsx)
and the Pages Router fallback meta (src/pages/_app.tsx).

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 14:31:37 +02:00
Huang Xin 712d564e9d feat(sync): encrypted OPDS credentials + Tauri keychain (PR 4c + 4d) (#4090)
* feat(sync): encrypt opds_catalog credentials end-to-end (TS path)

Wires encrypted-credential sync for opds_catalog via the CryptoSession
shipped in PR 4a (#4084) plus a new publish/pull crypto middleware.
TS-only — native still uses ephemeral storage (re-enter passphrase per
launch); PR 4d wires the OS keychain.

- ReplicaAdapter gains optional `encryptedFields: readonly string[]`.
  Adapters stay sync; the middleware handles the crypto round trip.
- replicaCryptoMiddleware.ts: encryptPackedFields drops the named
  fields from the push when the session is locked (no plaintext
  leak); decryptRowFields drops them on pull failure (local
  plaintext preserved by the store merge).
- replicaPublish / replicaPullAndApply invoke the middleware.
- OPDS adapter declares encryptedFields = [username, password] and
  now pack/unpack them as plaintext.
- passphraseGate.ts: ensurePassphraseUnlocked coalesces concurrent
  calls, prompts via the registered prompter with kind=setup|unlock,
  throws NO_PASSPHRASE on cancel.
- PassphrasePromptModal mounted at the Providers root; registers
  itself as the gate prompter.
- CryptoSession.forget() wipes server-side envelopes + salts.
- Migration 010 + replica_keys_forget RPC; DELETE
  /api/sync/replica-keys + client wrapper.
- SyncPassphraseSection on the user page: status / Set / Unlock /
  Lock / Forgot.
- CatalogManager pre-save: ensurePassphraseUnlocked when credentials
  are present; user cancel saves locally without sync.

Plan updated: PR 4 split documented as 4a/4b/4c/4d.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(sync): persist sync passphrase via OS keychain (Tauri)

Replaces the EphemeralPassphraseStore stub on native with real
OS-keychain storage so users don't re-enter their sync passphrase
every launch. Web stays on the in-memory ephemeral store by design.

Native bridge plugin gains 4 commands wired across all platforms:

- Rust desktop (`keyring` crate): macOS Keychain on apple-native,
  Windows Credential Manager on windows-native, Linux libsecret/
  Secret Service on sync-secret-service. Per-target features so each
  platform compiles only the backend it needs.
- iOS Swift: Security framework Keychain (kSecClassGenericPassword,
  SecItemAdd / Copy / Delete).
- Android Kotlin: androidx.security EncryptedSharedPreferences
  (AndroidKeystore-derived AES-GCM master key,
  AES256_SIV / AES256_GCM key/value encryption).

TS layer:

- TauriPassphraseStore wraps the bridge calls. set is fail-loud
  (surfaces keychain rejection); get is fail-soft (returns null on
  any error so the gate prompts).
- createPassphraseStore returns ephemeral synchronously;
  upgradeToKeychainIfAvailable swaps the singleton to
  TauriPassphraseStore on Tauri after probing the bridge. CryptoSession
  resolves the store via createPassphraseStore() each touch so the
  swap is transparent.
- CryptoSession.tryRestoreFromStore: silent unlock at boot. Stale-
  entry recovery clears the store when the account has no salt
  server-side. unlock/setup persist; forget also clears the store.
- Providers boot effect: upgrade keychain → silent restore.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(sync): make encrypted-credential pull actually decrypt + UX polish

PR 4c shipped the encrypt path but the pull side silently dropped
ciphers when locked, the modal was busy with double-rings, and web
re-prompted on every page refresh. This rolls up the post-test
fixes + UX polish:

Pull-side decrypt:
- decryptRowFields takes an `onLocked` callback the orchestrator
  wires to the passphrase gate; encountering a cipher field with a
  locked session now triggers the lazy-prompt path instead of
  dropping the field.
- replicaPullAndApply re-applies the unpacked row for metadata-only
  kinds even when a local copy exists, so the now-decrypted creds
  reach the store (the binary-kind skip-if-local optimization
  doesn't apply).
- Cipher fingerprint comparison: capture the row's `cipher.c` for
  each encrypted field, compare against the local record's
  lastSeenCipher. Same → skip prompt + decrypt entirely. Different
  (rotation / value change on another device) → prompt to
  re-decrypt. Fingerprint persists via OPDSCatalog.lastSeenCipher.

Web persistence:
- SessionStoragePassphraseStore: passphrase survives page refresh
  within the same tab, dies on tab close. Replaces
  EphemeralPassphraseStore as the default on web. Avoids
  localStorage / IndexedDB to keep the tab-scoped trust boundary.

UI:
- Renamed PassphrasePromptModal → PassphrasePrompt; modernized: filled
  input style with single subtle focus border, btn-primary +
  btn-ghost replaced with leaner custom buttons. eink-bordered +
  btn-primary classes give the dialog correct e-paper rendering.
- globals.css: suppress redundant outline/box-shadow on focused text
  inputs / textareas (the element's own border is the focus
  indicator).
- AGENTS.md: documents the e-ink convention (`eink-bordered`,
  `btn-primary` for inverted CTAs, etc.) so future widgets ship with
  e-paper support.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 13:22:49 +02:00
Huang Xin 6bfeb295d2 feat(sync): add opds_catalog replica kind (plaintext fields) (#4087)
Wires OPDS catalogs through replica sync as a metadata-only kind.
Plaintext fields only in this PR — encrypted credentials (username,
password) ship in the follow-up alongside the SyncPassphrasePanel UI
and Tauri keychain backend.

- Migration 009 extends the kind allowlist with 'opds_catalog'.
- replicaSchemas adds opdsCatalogFieldsSchema (name, url, description,
  icon, customHeaders, autoDownload, disabled, addedAt) with a 50-row
  per-user cap.
- New opdsCatalogAdapter is metadata-only (no `binary` capability).
  Stable cross-device id from md5("opds:" + url.lower()) so two
  devices that import the same URL converge to one row instead of
  duplicating.
- New customOPDSStore (zustand) hydrates from SystemSettings,
  publishes upserts/deletes through the replica pipeline, preserves
  local-only username/password when overlaying remote updates, and
  strips tombstones at the persistence boundary so existing
  useSettingsStore readers (useOPDSSubscriptions, pseStream,
  app/opds/page.tsx) need no migration.
- replicaPullAndApply branches on adapter.binary so metadata-only
  kinds skip the bundleDir requirement and the manifest/binary path.
- CatalogManager rewires Add / Edit / Remove / Toggle / Add-popular
  through the new store.

Plan update bundled in: tenet 8 (scalar settings sync via a bundled
row; collections sync per-record), per-kind allowlist now includes a
`settings` singleton that will collapse PRs 5 + 6+ into one bundled
adapter, and PR 4 is split into 4a (already merged) / 4b (this) / 4c
(encrypted credentials + UX).

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 05:33:18 +02:00
Huang Xin aea3fda086 chore: bump turso to the latest version (#4086) 2026-05-07 20:11:47 +02:00
Huang Xin 35227ecd61 feat(sync): wire crypto session for encrypted-field sync (#4084)
Adds the per-account PBKDF2 salt endpoint and an in-memory CryptoSession
that derives keys lazily per saltId. Lets future kinds (OPDS catalogs in
PR 4b) encrypt/decrypt fields without re-deriving on every operation.

- Migration 008: replica_keys_{create,list} RPCs round-trip the bytea
  salt as base64; SECURITY INVOKER, RLS-gated by the existing replica_keys
  policies.
- /api/sync/replica-keys GET/POST endpoint matches the dual app/pages
  shape used by /api/sync/replicas.
- ReplicaSyncClient.{listReplicaKeys,createReplicaKey} wraps the endpoint.
- CryptoSession.{unlock,setup,encryptField,decryptField,lock} caches
  derived keys per saltId; foreign envelopes trigger a lazy re-list +
  derive. Iterations injectable so tests run with PBKDF2 ITER=1000.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 20:05:04 +02:00
Huang Xin cb30716830 refactor(hardcover): move note mappings to SQLite on web and native (#4083)
Hardcover's note → journal-id mapping store was split-brained: SQLite on
Tauri, localStorage on Web. Unify on SQLite for both. Existing localStorage
entries are migrated lazily on first loadForBook(bookHash) and removed.

Wiring up SQLite on Web exposed two latent gaps:

- @tursodatabase/database-wasm needs SharedArrayBuffer, which requires
  cross-origin isolation. next.config.mjs now sets COOP/COEP headers.
- The WASM connector calls getFileHandle(name) directly under
  navigator.storage.getDirectory() and does not traverse subdirectories,
  so paths like "Readest/hardcover-sync.db" raise "Name is not allowed".
  webAppService.openDatabase now flattens the resolved path to a single
  OPFS-safe segment before opening.

Also catches up two i18n keys (`{{percentage}}% used`,
`Resets in {{duration}}`) added by the daily-reset countdown feature.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 11:28:03 +02:00
Huang Xin 4625e47a6d refactor(sync): extract shared adapter / pull-deps / legacy-migration primitives (#4081)
Three kinds (dictionary, font, texture) of replica sync now visibly
duplicate each other; this PR extracts the shared shape now that the
abstraction is well-validated, per the plan's "extract only after the
second kind validates" guidance.

- New `services/sync/adapters/_helpers.ts` — `unwrap` field-envelope,
  `singleFileFilenameFromManifest`, `singleFileBinaryEnumerator`, and
  a default `computeId` for kinds with `record.contentId`. Wired into
  font + texture adapters (dictionary stays bespoke — multi-file
  enumeration and a different identity recipe).
- `useReplicaPull.ts` collapses the three near-identical
  `buildXPullDeps` (~80 lines each) into a single
  `buildReplicaPullDeps<T>` factory plus three small `ReplicaPullConfig`
  records (~12 lines each). Dispatch stays a typed `switch` to keep
  the generic record type sound under contravariance.
- New `services/sync/migrateLegacy.ts` — `migrateLegacyReplicas<T>`
  helper that owns the rehash-flat-path → `<bundleDir>/<filename>`
  migration. `migrateLegacyFonts` and `migrateLegacyTextures` are now
  thin per-kind configs, ~15 lines each (down from ~60).

Net: +143 / -291 across 5 files plus 2 new helpers. No behavior
change; 3933 tests still pass.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 10:19:23 +02:00
Huang Xin 77a85cee09 feat(account): show daily reset countdown under translation quota bar (#4082)
Adds a row beneath the translation characters bar on the user profile
page with "X% used" (start) and "Resets in H hr m min" (end). The
countdown points to the next UTC midnight, matching the server-side
daily-usage key in UsageStatsManager. Formatting goes through the dayjs
duration plugin and ticks every minute while the page is open.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 10:15:31 +02:00
Huang Xin 53936ad17c chore(i18n): translate replica-sync File-toast strings (#4080)
Translates the six "File ..." transfer-toast strings introduced in
#4077 (File uploaded / File downloaded / Deleted cloud copy / and the
three Failed-to variants) across 33 locales. Each translation models
the locale's existing "Book ..." copy with book → file/document and
backup → copy.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 10:10:46 +02:00
Huang Xin de6529523f feat(sync): cross-device background texture sync (#4079)
Plug the texture replica adapter into the kind-agnostic primitives
shipped in #4077. Textures imported on one device download and
become available on every signed-in device, with the same shape as
the font sync stack (single-file binary, contentId from
partialMD5+size+filename, bundleDir layout, replica-publish on
import, full activation on auto-download).

Includes legacy flat-path migration so pre-existing textures sync
without re-import, ColorPanel import flow now publishes the row
and queues the binary upload, and createCustomTexture preserves
contentId/bundleDir/byteSize through addTexture (mirrors the
font-import fix). Server allowlist gains 'texture' with a
single-image Zod schema; useBackgroundTexture passes replica
metadata through addTexture so the boot-time "ensure selected
texture is in store" path doesn't silently un-publish a remote
record.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 09:55:32 +02:00
Huang Xin ca674bb5e7 chore(agent): update project memory (#4078) 2026-05-07 08:33:53 +02:00
Huang Xin 981579c255 feat(sync): cross-device custom font sync (#4077)
* refactor(sync): kind-agnostic replica primitives

Extract dict-only sync into shared primitives (registry, pull/apply
orchestrator, persist env, schema allowlist) so other kinds can plug
in. Companion changes: per-replica Storage Manager grouping,
useReplicaPull boot-race recovery, manifest=null reconciliation on
every boot pull, copyFile takes explicit srcBase + dstBase, settled-
event helpers, lenient webDownload Content-Length (R2/S3 signed URLs
commonly omit it), and generic "File" transfer toast copy any replica
kind can share.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(sync): cross-device custom font sync

Plug the font replica adapter into the kind-agnostic primitives:
font store gains replica wiring, custom font import publishes the
replica row + queues a binary upload, and bootstrap registers the
font adapter and download-complete handler.

Includes legacy flat-path migration so pre-existing fonts sync
without re-import, full @font-face activation on auto-download
(load + mount the rule, mirroring manual import), and a fix to
createCustomFont so contentId / bundleDir / byteSize survive the
trip through addFont — otherwise import-time publish silently
no-oped on missing contentId.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 08:28:44 +02:00
Huang Xin cbdc3b8f52 feat(sync): wire dictionary store through replica sync (follow-up to #4075) (#4076)
* feat(sync): cross-device dictionary sync

Custom MDict / StarDict / DICT / SLOB dictionaries now sync across
signed-in devices via the replica layer.

- Store mutations publish replica rows with field-level LWW + tombstones.
- Re-importing the same content (renamed or after delete) preserves the
  user's label and reincarnates the server row instead of duplicating.
- Manifest commits after binary upload so other devices never see a row
  whose binaries aren't on cloud storage yet.
- Pull-side orchestrator creates a placeholder dict, queues the binaries
  via TransferManager, and clears the unavailable flag on completion.
- Toast copy branches by transfer kind so dict uploads don't read
  "Book uploaded".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(sync): boot pull and binary download path

- Defer the boot pull until TransferManager is initialized so download
  enqueues aren't dropped.
- Auto-persist the local dict store after applyRemoteDictionary; otherwise
  the next loadCustomDictionaries wipes the in-memory rows.
- Boot pull passes since=null so a device whose cursor advanced past
  unpersisted rows can still recover.
- Skip pulling when not authenticated instead of logging
  "SyncError: Not authenticated" on every boot of a signed-out device.
- downloadReplicaFile resolves the destination against the kind's base
  dir; binaries previously landed at the literal lfp and openFile then
  failed with "File not found".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(sync): per-page useReplicaPull hook

Lifts the boot-time pull out of EnvContext into a hook each page mounts
for the kinds it needs: useReplicaPull({ kinds: ['dictionary'] }).
Library page and the shared Reader component opt in. The hook fires 10s
after page load (so feature mounts hydrate first), dedups per-kind
across navigation, and releases the slot on failure so a later mount
can retry. Future kinds plug into the hook's per-kind switch.

Also closes two refresh-loop bugs:

- Hydrate the dict store from settings BEFORE the apply loop, so the
  auto-persist doesn't clobber persisted rows that the in-memory store
  hadn't yet read. Library-page refresh was the visible victim.
- Skip the download queue when every manifest file is already on disk
  under the resolved bundle dir. Refreshing is a no-op; partial-
  download recovery still queues because some files would be missing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 21:39:38 +02:00
Huang Xin 3b348c8f35 feat(sync): CRDT replica sync foundation (#4075)
* feat(sync): foundation for CRDT-based cross-device replica sync (Phase 1+2)

Adds the primitives and orchestration layer for syncing user-imported
assets (dictionaries, fonts, textures, OPDS catalogs, dict settings)
across devices via a polymorphic `replicas` table with field-level LWW
under HLC ordering. Phase 1 ships the foundation (CRDT, crypto, server
schemas, SQL migrations, push/pull endpoint); Phase 2 adds the adapter
registry, HTTP client, and sync manager. No modifications to existing
book sync — additive only.

Phase 1:
- src/libs/crdt.ts — HlcGenerator (monotonic + remote-absorption +
  clock-regression-safe), per-field LWW with deviceId tiebreak,
  remove-wins tombstones, reincarnation token revival.
- src/libs/crypto/{derive,encrypt,envelope,passphrase}.ts —
  PBKDF2-600k key derivation (OWASP 2024), AES-GCM round-trip,
  envelope {c,i,s,alg,h} with SHA-256 sidecar integrity check,
  passphrase storage abstraction (web ephemeral; Tauri keychain stub).
- src/libs/replica-schemas.ts — Zod-backed allowlist (dictionary only
  in PR 1), 64KiB row cap, 64-field cap, schemaVersion bounds,
  filename validator.
- src/libs/replica-sync-server.ts — push batch validation
  (auth + allowlist + schema + HLC ±60s skew clamp).
- src/pages/api/sync/replicas.ts — POST/GET endpoint wrapping the
  Postgres crdt_merge_replica function via RPC.
- docker/volumes/db/migrations/003_add_replicas.sql — replicas table
  + replica_keys table + RLS.
- docker/volumes/db/migrations/004_crdt_merge_replica_fn.sql — atomic
  per-field LWW merge function (forwards-compat preserves unknown
  fields).

Phase 2:
- src/services/sync/replicaRegistry.ts — adapter contract
  (core + optional BinaryCapability + LifecycleHooks per eng review).
- src/libs/replica-sync-client.ts — HTTP wrapper mapping status codes
  to typed SyncError codes.
- src/services/sync/replicaSyncManager.ts — 5s debounced push,
  immediate flush on visibilitychange/online, per-kind pull cursor,
  remote HLC absorption.

Tests: 125 new (crdt 26, crypto 32, schemas 21, server 16, client 12,
registry 6, manager 12). Full suite 3656 passing, lint clean. Existing
book/config/note sync paths untouched.

Plan: ~/.claude/plans/vivid-orbiting-thimble.md
CEO plan: ~/.gstack/projects/readest-readest/ceo-plans/2026-05-06-replica-sync-cathedral.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(sync): add kind="replica" path through TransferManager (Phase 3)

Adds the replica branch to the existing book-shaped transfer
infrastructure so dictionary (and future kinds) bundles can flow through
the same queue, retry, and progress UI as book uploads.

Existing book transfer paths remain unchanged. The book-side regression
suite (37 tests in transfer-store.test.ts, 37 in transfer-manager.test.ts)
all stay green.

Store (src/store/transferStore.ts):
- TransferItem gains kind: 'book' | 'replica' (default 'book' on legacy
  persisted rows), replicaKind, replicaId, replicaFiles, replicaBase.
- New addReplicaTransfer(replicaKind, replicaId, displayTitle, type, opts)
  with files + base in opts; auto-computes totalBytes from file sizes.
- New getReplicaTransfer(replicaKind, replicaId, type) lookup.
- getTransferByBookHash filters to kind === 'book' (defensive against
  bookHash="" collisions on replica items).
- restoreTransfers fills kind: 'book' for legacy persisted rows.

Manager (src/services/transferManager.ts):
- queueReplicaUpload / queueReplicaDownload / queueReplicaDelete.
- executeTransfer dispatches by kind to the new executeReplicaTransfer
  (iterates files, calls appService.uploadReplicaFile per file with
  per-file progress aggregation) or the existing executeBookTransfer
  (refactored out, byte-identical behavior).
- Dispatches replica-transfer-complete event on success so stores can
  react (e.g., commit manifest_jsonb to the replica row).

Storage / cloud (src/libs/storage.ts, src/services/cloudService.ts):
- uploadReplicaFile bypasses the book-only File.name smuggling and
  takes an explicit cfp (cloud file path).
- uploadReplicaFileToCloud / downloadReplicaFileFromCloud /
  deleteReplicaBundleFromCloud orchestrate per-file operations under
  ${userId}/Readest/replicas/<kind>/<replicaId>/<filename>.
- replicaCloudKey() centralizes the path-construction rule.
- New CLOUD_REPLICAS_SUBDIR constant.

App service (src/services/appService.ts, src/types/system.ts):
- AppService gains uploadReplicaFile, downloadReplicaFile,
  deleteReplicaBundle (file-level operations; orchestration lives in
  TransferManager).

Tests: 18 new (12 in transfer-store.test.ts, 5 in transfer-manager.test.ts,
1 fixture). Full suite 3674 passing, lint clean. Existing book regression
clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(sync): dictionary replica adapter + bootstrap (Phase 4, partial)

Lands the safe-to-ship foundation of Phase 4 — adapter logic, registry
bootstrap, and SystemSettings hooks for replica sync. The on-disk
migration of legacy customDictionaries (bundleDir → content-hash id),
the live store wiring, and the Settings → Sync UI are deferred to a
follow-up PR so they can land with real-device QA.

Adapter (src/services/sync/adapters/dictionary.ts):
- dictionaryAdapter: kind='dictionary', schemaVersion=1.
- pack/unpack — only synced subset (name, kind, lang, addedAt,
  unsupported{,Reason}). bundleDir / files / unavailable / deletedAt
  stay per-device or are handled by the tombstone mechanism.
- BinaryCapability.enumerateFiles dispatches by bundle kind:
  - mdict:    mdx + mdd[] + css[]
  - stardict: ifo + idx + dict + syn (skips .idx.offsets / .syn.offsets
              sidecars — those are device-local indices)
  - dict:     dict + index
  - slob:     single .slob file
- primaryDictionaryFile() picks the anchor file per kind for
  partialMD5 hashing.
- computeDictionaryReplicaId(partialMd5, byteSize, sortedFilenames)
  produces a deterministic 32-hex content-hash id used at import time.
- 23 tests cover pack/unpack identity, kind dispatch, file enumeration,
  id determinism, and per-device-field exclusion.

Bootstrap (src/services/sync/replicaBootstrap.ts):
- bootstrapReplicaAdapters() registers all known adapters once at app
  start. Idempotent (safe to call multiple times). Wired into
  EnvContext.tsx so the registry populates on app mount.
- 3 tests cover registration, idempotency, and the PR-1 allowlist.

SystemSettings (src/types/settings.ts, src/services/constants.ts):
- +SyncCategory = 'book' | 'progress' | 'note' | 'dictionary' — typed
  union for the user-facing sync toggles. 'progress' gates the
  existing book-config sync (reading progress); 'note' gates
  annotations; 'book' gates book binaries + metadata; 'dictionary'
  gates the new replica sync. Future replica kinds extend the union.
- +SYNC_CATEGORIES readonly array for UI iteration.
- +syncCategories: Partial<Record<SyncCategory, boolean>> — per-
  category opt-in toggles in DEFAULT_SYSTEM_SETTINGS (default ON for
  all four). UI panel ships in the follow-up.
- +lastSyncedAtReplicas: Record<string, string> — per-kind HLC pull
  cursors (matches replicaSyncManager's CursorStore contract).

Registry type cleanup (src/services/sync/replicaRegistry.ts):
- BinaryCapability.enumerateFiles return shape: localRelPath → lfp
  to match the existing TransferStore.ReplicaTransferFile convention.

Tests: 28 new (23 dict + 3 bootstrap + 2 syncCategories defaults).
Full suite 3702 passing, lint clean. Existing book/config/note sync
paths untouched.

Deferred to PR 1 follow-up (with real-device QA):
- customDictionaryStore migration: rehash legacy uniqueId() bundleDir
  to content-hash id; preserve providerOrder mapping; staged
  .legacy/<old-id>/ backup.
- Wire customDictionaryStore mutations through replicaSyncManager
  (markDirty on add/rename/delete; pull on init).
- Settings → Sync panel: per-category toggles + last-sync timestamps.
- Sync passphrase modal: set / change / forgot flow (lazy first prompt
  on encrypted-field push/pull).
- <CloudReplicaRow> in CustomDictionaries.tsx for "Download from cloud
  (X MB)" affordance.
- Tauri keychain backend for sync passphrase storage.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(sync): replicaSync singleton + content-hash id at dict import (Phase 4b)

Two foundation pieces that everything UI-side will sit on top of, both
purely additive — no live store wiring, no behavior change for existing
dictionary imports.

replicaSync singleton (src/services/sync/replicaSync.ts):
- initReplicaSync({deviceId, cursorStore, hlcStore?, client?}) builds
  one ReplicaSyncManager backed by an HlcGenerator with persistence
  wrapped around .next()/.observe(). Idempotent (second init returns
  the existing instance).
- LocalStorageHlcStore (src/libs/hlc-store.ts) snapshots the HLC
  counter under 'readest_replica_hlc' so it survives restart. Falls
  back silently when localStorage is unavailable (private mode, SSR);
  client re-derives via the existing remote max(updated_at_ts) repair
  path. InMemoryHlcStore is the test backend.
- 16 tests (9 hlc-store, 7 replicaSync) covering snapshot persistence,
  restore-on-init, and idempotency.
- Wiring into EnvContext for production deferred to the follow-up that
  also adds the cursor store backed by useSettingsStore.

Content-hash id at dictionary import
(src/services/dictionaries/contentId.ts):
- computeDictionaryContentId(primaryFile, filenames) wraps
  computeDictionaryReplicaId(partialMd5(primary), byteSize,
  sortedFilenames) — the cross-device id used as the replica_id when
  the dict actually pushes/pulls.
- Wired into all four import paths in dictionaryService.ts:
  - stardict primary = .ifo (small text, partialMD5 ≈ full hash)
  - mdict primary    = .mdx (body)
  - dict primary     = .dict.dz (gzipped body)
  - slob primary     = .slob (single-file bundle)
  ImportedDictionary gains contentId?: string. Optional for backwards
  compat; legacy bundles without contentId are flagged as
  "needs rehash before sync" by the upcoming store-wiring follow-up.
- 6 tests cover identity determinism, byteSize sensitivity, filename-
  set sensitivity, and order-independence.

Tests: 22 new (9 + 7 + 6). Full suite 3724 passing, lint clean.
Existing dictionary import flow unchanged for users — contentId is an
additional field, not a replacement for the bundleDir-based id.

Deferred to follow-up (with on-device QA):
- Production cursor store backed by useSettingsStore +
  appService.saveSettings.
- EnvContext call to initReplicaSync after appService boot.
- customDictionaryStore mutation hooks → replicaSyncManager.markDirty.
- Legacy bundleDir → contentId migration with .legacy/ backup.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(sync): land replica-sync design plan in repo

Moves the plan document that drove this PR's foundation work
(`~/.claude/plans/vivid-orbiting-thimble.md`) into the project tree at
`apps/readest-app/.claude/plans/` so reviewers and future contributors
can read it alongside the code without leaving the repo.

The plan went through three review passes — Codex (19 findings, all
absorbed), CEO/scope review (mode SCOPE EXPANSION; encrypted secrets
pulled forward to v1, "private-only forever" posture lock), and eng
review (FULL_REVIEW mode, 16 findings absorbed). The full review trail
lives in the file's `## GSTACK REVIEW REPORT` section at the bottom.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(crdt): point README at in-repo plan path

Now that vivid-orbiting-thimble.md lives at
apps/readest-app/.claude/plans/, the README link should point there
rather than at the home-dir copy that no longer exists.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(sync): rename src/libs files to camelCase per project convention

Test files renamed in lockstep. Imports + comment references updated
across cloudService, storage, transferManager, replicaSync,
replicaSyncManager, /api/sync/replicas, and all four test files. No
behavior change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 15:50:15 +02:00
Huang Xin dd0ff6ae9d chore(agent): bump gstack (#4073) 2026-05-06 10:57:51 +02:00
Huang Xin 30dee7b909 feat(dict): improve MDict rendering and dictionary management (#4072)
* fix(reader): play sound:// links in MDict definitions via MDD lookup

MDX entries reference audio resources with `<a href="sound://name.ext">`.
Until now those anchors fell through to the browser, which tried to
navigate to an invalid scheme and did nothing useful.

Wire each `sound://` anchor inside the rendered MDX body to:
- preventDefault + stopPropagation (so the parent card's tap-to-expand
  doesn't fire),
- look up the path in every companion `.mdd` until one returns bytes
  (js-mdict's `MDD.locateBytes` auto-normalizes the leading separator),
- wrap the bytes in a Blob and play via `new Audio(URL.createObjectURL)`,
- cache the resolved URL on the anchor so subsequent clicks reuse it,
  with the URL tracked for revocation in `dispose()`.

Note: many MW-style dictionaries use `.spx` (Speex) which Chromium and
Safari don't natively decode — the lookup will succeed but playback may
fail silently. Other formats (mp3, wav, ogg vorbis) play fine.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(dict): improve MDict rendering and dictionary management

Builds on the sound:// fix to round out MDict rendering and tighten
the dictionary settings panel.

MDict provider:
- Follow MDict-specific URL schemes inside the rendered HTML:
  `sound://path` plays via Audio (with a deprecation toast for `.spx`
  whose codec no major browser decodes), and `entry://word` /
  `bword://word` forward to ctx.onNavigate so the popup re-looks-up
  the target. Cycle-bounded (5 hops) `@@@LINK=<word>` content-level
  redirects are followed transparently, so entries that are pure
  redirect strings (e.g. "questions" → "question") render the
  canonical entry instead of the literal redirect text.
- Render the body inside a shadow root so each dict's CSS stays
  scoped — `<link rel="stylesheet">` references are resolved against
  the companion .mdd, loose .css files imported alongside the bundle
  are read at init, and `url(...)` refs inside both are rewritten to
  blob URLs sourced from the MDD (covers sound icons, background
  images, @font-face sources). The body is tagged `data-dict-kind="mdict"`
  for downstream targeting.
- A baseline app-level stylesheet (`getDictStyles`) is injected into
  every shadow root with theme-adaptive `mix-blend-mode` for `<a>`
  background icons / `<a> img` (multiply on light, screen on dark);
  isDarkMode is forwarded via the lookup context.
- `<img src="/path">` is now treated as MDD-relative (the tightened
  IMG_SRC_PROTOCOL_RX skips schemes / protocol-relative only); a
  fallback retry strips the leading slash for bundles that store the
  resource without it.
- The auto-prepended light-DOM headword `<h1>` is hidden when the
  dict body either leads with a same-text element (any tag — covers
  `<h3 class="entry_name">`, etc.) or contains an `<h1>` with the same
  trimmed text anywhere (covers wrapper-div-then-h1 layouts).

Dictionary management:
- Importing a dict whose name matches an existing one now replaces
  it in place, preserving the slot in providerOrder and inheriting
  the previous enabled flag. The .css extension is added to the file
  picker, and loose .css files imported alongside .mdx/.mdd are
  bundled with the dictionary regardless of stem-match.
- The settings panel gains an Edit mode (parity with Delete mode):
  trailing pencil button on imported dicts and custom web searches
  opens a rename modal. Edit and Delete are mutually exclusive.
  Below 400px, the Edit/Delete labels collapse to icons only.

Card UX:
- The card's tap-to-expand handler now walks `composedPath()` so
  clicks on anchors / buttons / images inside the shadow root no
  longer fold the card.

i18n:
- Translations added for new strings across 33 locales.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 08:27:58 +02:00
Huang Xin a272ba892a feat(reader): replace dictionary tabs with stacked result cards (#4071)
Redesign the dictionary lookup UI as a single scrolling list of
expandable cards — one per provider that has a result — instead of
a tab strip with one active provider at a time.

Behavior:
- All enabled dictionaries are queried in parallel; cards render in
  user-defined order. Cards whose provider returns no result, an
  unsupported format, or an error are removed entirely.
- Cards default to expanded when 3 or fewer providers have results,
  collapsed (4-line preview) otherwise. Manual taps are sticky
  across re-renders; the auto-decision is reset only when a new
  word is looked up.
- Web-search providers (Google, Urban, Merriam-Webster, custom
  templates) appear in a separate "Search the web" section as
  tappable rows. On the web build they use native target="_blank"
  anchors; on Tauri the click is routed through openUrl since
  target="_blank" doesn't open externally there.
- The header carries a back arrow (when in-content link navigation
  has pushed onto the history stack), the looked-up word, and a
  gear that deep-links to Settings → Language → Dictionaries.

Mobile / narrow viewports (<sm) get the same UX as a bottom sheet
(Dialog with snapHeight 0.75); sm+ viewports keep the anchored
popup with triangle pointer. Both share useDictionaryResults +
DictionaryResultsHeader/Body.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 04:30:30 +02:00
Huang Xin e7f370453e fix(layout): resolve layout issues with mixed writing modes in adjacent sections (#4069) 2026-05-05 19:59:13 +02:00
Huang Xin 5dc2528455 fix(library): support dropping directories to import books (#4068)
Drag-and-drop now classifies dropped items into files vs directories.
Real files keep the existing import flow; dropped directories reuse
handleImportBooksFromDirectory via a new import-book-directory event,
matching the "From Directory" menu behavior instead of failing with
"No supported files found".

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 19:42:16 +02:00
Huang Xin c27245e980 feat(reader): support deeplink and web link in annotation export (#4067)
Expose `annotation.appLink` (readest://) and `annotation.webLink`
(https://web.readest.com) as template variables for custom export
templates. The shipped default template now emits the readest:// app
deeplink for the page link so exported notes open the native app.
The non-template export mode keeps the universal https link.

Preview links also gain target="_blank" so they open in a new tab
instead of replacing the dialog.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 19:39:26 +02:00
Huang Xin 9b0072173c feat(metadata): parse Calibre series info from PDF and CBZ (#4066)
Surface series name and index from Calibre-written metadata so the
existing belongsTo.series → metadata.seriesIndex pipeline picks them up.

- PDF: read calibre:series + calibreSI:series_index from XMP
- CBZ: read ComicInfo.xml (Series/Number/Count); fall back to
  ComicBookInfo/1.0 (series/issue) in the ZIP comment

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 18:32:39 +02:00
Huang Xin 06aec0b597 fix(reader): revert footer to default visibility when tap-to-toggle is disabled (#4065)
Tapping the footer with `tapToToggleFooter` on cycles `progressInfoMode`
through values including 'none', which persists to view settings. When
the user later disabled the toggle in settings, nothing reverted the
saved mode — so the footer stayed hidden with no UI path back to a
visible state, only re-enabling the toggle and tap-cycling forward.

ProgressBar now self-heals: when `tapToToggleFooter` is off and the
current mode isn't already 'all', it resets to 'all'. Fires both at
mount (book opened with stuck 'none') and on the toggle transition.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 17:27:11 +02:00
Huang Xin 15c0a7a2f2 fix(koplugin): render group cover previews in Library (#4064)
Group cells in the Readest Library view rendered as FakeCover even when
their child books had perfectly good cloud covers — the queue that
fetches <hash>.png covers was only primed for cloud-only book entries
on the visible page, so children of group entries were never requested.
A later partial composite (3/4 covers) was also written to a
content-fingerprinted disk cache and kept serving forever, since the
fingerprint stayed the same after the 4th cover landed.

Two fixes wired together:
- libraryitem.set_visible_hashes now expands visible group entries to
  include their first-N children's hashes, so trigger_download's
  visibility filter no longer rejects them.
- group_covers.child_cover_bb queues a cloud-cover download when the
  fallback path misses for a cloud-present book. Capped at 4 per group
  by the existing cells_for(shape) limit.

Disk caching of composites is dropped entirely; mosaics are recomposed
in memory each paint. New spec/library/group_covers_spec.lua locks the
contract for URI round-trip, child_cover_bb's missing-cover branches,
and libraryitem's group-children expansion.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 17:01:35 +02:00
Huang Xin d66fedcab7 feat(reader): manage rules shortcut in proofread popup (#4062) 2026-05-05 16:01:26 +08:00
Huang Xin 43f72720f2 feat(i18n): add Uzbek and Brazilian Portuguese translations (#4061)
* feat(i18n): add Uzbek (Oʻzbek) translation

Adds `uz` as a first-class supported locale across the Readest app
and the readest.koplugin companion. Also refactors the supported
locale set to source from a single ground-truth file
(`apps/readest-app/i18n-langs.json`) consumed by both the i18next
runtime and the i18next-scanner config, and replaces a NUL-byte
sentinel in `extract-i18n.js#unescapePo` with a single-pass regex
so git no longer treats the script as binary.

Closes #4053

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(i18n): add Brazilian Portuguese (pt-BR) translation

Adds `pt-BR` as a regional variant supported alongside `pt`. Falls back
to `pt` then `en` for any future missing keys, so European Portuguese
gracefully covers gaps. Translations follow Brazilian conventions
(arquivo / tela / excluir / salvar / baixar / senha, gerundive verb
forms) rather than verbatim copying the European catalog.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 09:20:44 +02:00
Huang Xin 6d29814551 chore(koplugin): refresh i18n catalogs (#4058)
Run scripts/extract-i18n.js — adds 70 new msgids covering the Library
view (View Mode / Group by / Sort by labels, action sheet entries,
search dialog, error toasts) and drops 1 obsolete msgid removed
during the picker rework. All 31 locales updated; new strings have
empty msgstrs awaiting translation.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 06:11:37 +02:00
Huang Xin ed8956e9ed feat(koplugin): Readest Library view in KOReader (#4056)
* feat(koplugin/library): data layer + busted harness + design doc

- LibraryStore: per-user SQLite index merging cloud + local books by
  partial-md5 hash. listBooks/listBookshelfBooks/listBookshelfGroups,
  upsertBook with cloud_present/local_present OR-merge + _force/clear
  sentinels, parseSyncRow, getChangedBooks for tombstone push.
- EXTS table mirroring web's document.ts.
- busted harness with KOReader stubs (G_reader_settings, DataStorage,
  lua-ljsqlite3 against :memory:); spec_helper, exts_spec, smoke_spec,
  librarystore_spec covering schema, sort, group nesting, dedupe.
- Library design doc + spec README.
- pnpm test:lua wired through root + app package.json; lint-koplugin
  recurses into library/ + spec/.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(koplugin/library): cloud sync — push, pull, upload, delete, downloads

- Spore methods: pullBooks (incremental /sync), getDownloadUrl,
  getUploadUrl, listFiles, deleteFile.
- syncbooks.lua: pushBook + pushChangedBooks (advances watermark to
  max(updated_at, deleted_at)), syncBooks(opts, mode) for push/pull/both,
  downloadBook + downloadCover (sync socket.http with file sink; cover
  download via fork+poll with single-slot queue + visible-page filter +
  coalesced refresh), uploadBook (presigned-PUT flow + best-effort
  cover), deleteCloudFiles (list-then-delete-each, mirrors
  cloudService.deleteBook).
- SyncAuth.withFreshToken wrapper resolves the ensureClient race; 401/403
  unified across syncconfig + syncannotations.
- Cloud + local book covers shared by partial-md5 hash; cover.png cached
  at <settings>/readest_covers/<hash>.png with sentinel for known 404s.
- syncbooks_spec covers row-to-wire conversion + file_key shape.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(koplugin/library): local discovery — sidecar scanner + cover provider

- localscanner.lightScan iterates ReadHistory entries, reads
  partial_md5_checksum from .sdr sidecars, and upserts local rows.
  Slow filesystem walks deferred to fullSidecarWalk (24h-gated).
- coverprovider wraps BookInfoManager:getBookInfo for local books with
  graceful FakeCover fallback when coverbrowser is absent.
- localscanner_spec + coverprovider_spec.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(koplugin/library): UI — widget, item, view menu, FileManager hooks

- librarywidget: full-screen Menu mixed in with CoverMenu + Mosaic/List
  per zen_ui's group_view pattern. Title-bar tap → view menu, search via
  left icon, drill-in/back for grouping. Async cloud sync deferred via
  scheduleIn so the menu paints before HTTP fires.
- libraryitem: BIM patch for cloud-only (readest-cloud://) and group
  (readest-group://) URIs; group-cover composer (2x2 mosaic for grid,
  same in list) with cache key derived from the actual first-N hashes
  for content-based invalidation; ListMenuItem update + paintTo patches
  for wider list-mode cover strip and cloud-up/down icon overlay.
- libraryviewmenu: ButtonDialog with View/Group by/Sort by/Actions.
  Default Group by = Groups (parity with web), values authors/groups.
- librarypaint: partial-page e-ink repaint shim adapted from zen_ui.
- main.lua: Library menu entry, dispatcher actions (Open Library / Push
  / Pull as general; progress + annotations as reader-only),
  "Add to Readest" button in FileManager's long-press file dialog
  (dedupe by partial_md5; bumps updated_at when present, inserts a
  fresh local-only row otherwise; un-tombstones via _clear_fields).
  Push books on Library open when auto_sync is on, pull-only otherwise.
- Long-press action sheet with Readest BookDetailView parity:
  Remove from Cloud & Device / Cloud Only / Device Only,
  Upload to Cloud, Download Book / Cover / All.
- Cloud-down + cloud-up SVG icons (LiaCloudDownloadAltSolid /
  LiaCloudUploadAltSolid) painted in the right-side wpageinfo slot.
- i18n catalog updated for new strings.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(koplugin/library): split libraryitem into focused modules

libraryitem.lua had grown to 1018 lines mixing five unrelated
concerns. Split along the natural seams:

  cloud_covers — readest-cloud:// URI scheme, on-disk <hash>.png
                 cache, single-slot async download queue, visible-page
                 filter
  group_covers — readest-group:// URI scheme, 2x2 mosaic composer with
                 content-derived cache key (first-N hashes), cell
                 layout table
  cloud_icons  — bundled cloud-up/cloud-down SVG loader, IconWidget
                 cache, paint-overlay positioning
  list_strip   — list-mode group row builder (4-cover wider strip
                 replacing ListMenu's square cover slot)
  bim_patch    — BookInfoManager:getBookInfo router (cloud / group /
                 local) + ListMenuItem update + paintTo patches; owns
                 the _library_local_paths set and orig BIM reference

libraryitem.lua is now 141 lines: just the entry-table constructors
(entry_from_row, entry_from_group, entry_back) plus thin install /
set_visible_hashes delegates. Each new module is 88-216 lines.

No behavior change — same 113 specs pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore(koplugin): co-locate dev tooling, ship-zip exclusions, CI job split

- apps/readest.koplugin/scripts/build-koplugin.mjs: local sideloading
  build with the same exclusions the release workflow uses.
- Move lint-koplugin + test-koplugin from apps/readest-app/scripts/ to
  apps/readest.koplugin/scripts/. All koplugin dev tooling now lives
  with the koplugin and is excluded from the published release zip.
- Rename to .mjs so Node treats them as ESM without the reparse warning
  (the i18n CommonJS scripts stay .js).
- Release workflow: zip -r exclusions for scripts/, docs/, spec/,
  .busted so dev artifacts don't ship to end users.
- PR workflow: split build_web_app into build_web_app + test_web_app
  for parallelism. The test job installs luarocks + busted +
  lsqlite3complete and runs pnpm test:lua. test-koplugin.mjs now
  hard-fails (instead of soft-skipping) when CI=true and a tool is
  missing — a broken CI toolchain previously exited 0 silently.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 23:12:34 +02:00
Huang Xin cead0f42e0 compat(css): fixed table layout and style in dark mode (#4055)
Closes #4028
Closes #4029
2026-05-04 17:36:03 +02:00
Huang Xin c59097b0ac feat(koplugin): add i18n catalog and sync info dialog (#4050)
- i18n loader at apps/readest.koplugin/i18n.lua: isolated callable,
  falls back to KOReader's gettext when a string is untranslated
- Translation catalog at locales/<i18next-code>/translation.po for 31
  languages, mirroring apps/readest-app/public/locales/
- scripts/extract-i18n.js (scan _("...") and _([[...]]); preserve
  existing, drop obsolete, add new) and scripts/apply-translations.js
  (bulk import from /tmp/koplugin-translations/<lang>.json)
- Mirror apps/readest-app SyncInfoDialog: rename showMetaHashInfo to
  showSyncInfo, dialog title "Sync Info", new Last Synced row computed
  as max(last_synced_at_config, last_synced_at_notes) from doc_settings
- syncconfig.lua / syncannotations.lua mark per-book sync timestamps
  on push/pull success
- Rename "Meta Hash" -> "Book Fingerprint" in koplugin and
  apps/readest-app SyncInfoDialog.tsx; translations propagated to
  all readest-app locales
- "book config" -> "reading progress" wording across user-facing
  strings (matches QiuYukang fork terminology)
- Replace "Log out as " / "Login failed: " concat prefixes with
  T(_("...%1..."), arg) placeholder pattern (RTL / verb-final friendly)
- pnpm lint:lua: luajit -b syntax check across koplugin .lua files;
  soft-skips when luajit is missing locally; CI installs luajit and
  runs the check unconditionally

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 05:35:36 +02:00
Huang Xin 7bb1133706 feat(dictionaries): add DICT/Slob formats and Web Search providers (#4048)
Extends the dictionary system beyond StarDict/MDict with two more open
formats and a pluggable Web Search tier so users can fall back to online
sources when their offline bundles miss a word.

Formats:
- DICT (dictd, RFC 2229): .index + .dict.dz bundles. Shared DictZip
  parsing with StarDict via new dictZip.ts helper.
- Slob (Aard 2): self-contained .slob containers, zlib-compressed
  utf-8 entries; non-zlib/non-utf-8 bundles flagged unsupported at
  import.

Web Search:
- Built-in templates for Google, Urban Dictionary, Merriam-Webster
  (seeded into providerOrder, disabled by default).
- Custom URL templates via %WORD% placeholder, URL-encoded at
  substitution; entries persist in settings.webSearches.
- V1 renders an "Open in {{name}}" external link (iframe embedding is
  blocked by every major target site's X-Frame-Options).

UI:
- CustomDictionaries panel: flat outline-primary buttons for Import /
  Add Web Search, end-aligned type badges for a uniform column,
  hover states, compact tips block.
- Dictionary popup: bottom-right Manage icon (tooltip-only) deep-links
  into Settings → Language → Dictionaries; rounded-corner clipping fix
  on the tab strip.

File picker accepts .index and .slob; importer recognizes DICT and
Slob bundles and reads bundle metadata for friendly names.

Tests cover DICT/Slob readers and providers with real freedict-eng-nld
fixtures, web search substitution + provider rendering, and the new
store CRUD for web searches.

Closes #4038
2026-05-03 20:07:27 +02:00
Huang Xin 8e7b2192d5 fix(reader): dismiss annotation popup on section info / progress bar tap (#4047)
Tapping the section info or progress bar overlays did not dismiss an
open annotation popup because the dismiss flow listens for
iframe-single-click, which only fires from inside the foliate iframe.
These overlays sit above the iframe and intercept taps before the
iframe sees them, so the popup stayed open.

Dispatch iframe-single-click first in their click handlers; if a popup
consumes it (existing useTextSelector handler dismisses popup/selection
and returns true), skip the original action.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 18:43:22 +02:00
Huang Xin e7564ffc27 fix(docker): correct Kong gateway port for client build arg (#4046)
The web client's NEXT_PUBLIC_SUPABASE_URL build arg pointed at port 7000,
but Kong is exposed on KONG_HTTP_PORT (8000 by default), so browser-side
Supabase calls failed in the self-hosted Docker setup.

Closes #4035

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 17:10:16 +02:00
Huang Xin e0fa6230ab feat(koplugin): support deletePluginSettings hook (#4045)
Implements the hook introduced by koreader/koreader#15240, called when
the user deletes the plugin via the plugin manager with "Also delete
plugin settings" checked. Removes the readest_sync entry from
G_reader_settings (auth tokens, user info, auto_sync flag, last_sync_at)
and resets the in-memory copy to defaults.

Closes #4039

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 16:31:02 +02:00
Huang Xin f5657fb3a0 fix(share): correct recipient import flow and assorted UI polish (#4043)
- ensureSharedBookLocal helper makes sure the local library has both the
  Book entry and the bytes on disk after /import succeeds; navigating
  into the reader before this lands on "Book not found"
- ShareLanding navigates via navigateToReader (path form on web) so the
  reader actually renders instead of hitting the App Router stub and
  going blank
- Loading + progress UI on the landing page while bytes stream in;
  Open-in-app disabled mid-import to avoid races
- UserInfo header: vertically center avatar with name/email, tighter
  mobile gap, and a fillContainer prop on UserAvatar so a parent can
  size the box via classes without the inline style fighting back
- Rename "Share current page" -> "Share reading progress" (and matching
  post-generation hint) and shrink the dialog from 480 to 460px
- Drop the unused Reload Page menu item from SettingsMenu
- Translate the two new i18n keys across 31 locales

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 16:16:46 +02:00
Huang Xin 19f2414f42 fix(share): hide download link on share landing page (#4041)
Direct file download from the public share landing carries rights /
abuse risk. Replace the Download button with the Open-in-app deep link
in both the logged-in flow (now: Add to library + Open in app) and the
anonymous flow (now: Open in app + Get Readest footnote).

The /api/share/[token]/download route is left intact so re-enabling
the button later is a one-file UI change.
2026-05-03 06:06:11 +02:00
Huang Xin 19f3e65b62 fix(share): make /s landing build under Next 16 layout-prop validation (#4040)
* fix(share): make /s landing build under Next 16 layout-prop validation

Production builds (`next build --webpack` for OpenNext on Cloudflare)
rejected the Layout default export with "Type 'LayoutProps' is not valid"
because Next 16 strictly enforces that layout components only accept
`{ children }` (and `{ params }` for dynamic segments) — never
`searchParams`. The previous design tried to read `searchParams` from
both the layout component AND its `generateMetadata`, but layouts don't
get `searchParams` at all (they're shared across child pages with
potentially different query strings).

Restructure:
- Move `generateMetadata` from `app/s/layout.tsx` to `app/s/page.tsx`,
  which DOES receive `searchParams`. Page is now a server component.
- Split the existing client component into `app/s/ShareLanding.tsx`
  (still `'use client'`); page.tsx wraps it in `<Suspense>` per the
  Next 16 client-component contract.
- Delete `app/s/layout.tsx` — no longer needed; the project's root
  layout still wraps everything.

Verified locally: `pnpm lint` clean, `pnpm build-web` green, all 9
share API routes plus `/s` show up in the route manifest.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(share): drop edge runtime from og.png so OpenNext can bundle it

OpenNext on Cloudflare errors out when bundling edge-runtime routes inside
the default server function:

  app/api/share/[token]/og.png/route cannot use the edge runtime.
  OpenNext requires edge runtime function to be defined in a separate
  function.

Splitting into a second function bundle is more config surgery than this
route deserves. `next/og`'s `ImageResponse` (Satori + WASM yoga/resvg) has
supported the default Node-compat runtime since Next 13.4, and on
Cloudflare via OpenNext the default function IS already a Worker, so
cold-start cost is similar to edge.

Verified: `pnpm exec opennextjs-cloudflare build` now completes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 04:40:16 +02:00
Huang Xin d1e7b4902c feat(share): time-limited share links with cfi-aware imports (#4037)
Add a Share Book feature that generates an expiring HTTPS share URL plus a
parallel readest://share/{token} deep link. Recipients land on /s/{token},
where logged-in users can one-tap "Add to my library" (R2 server-side
byte-copy) and anonymous users download the book or open it in the app.
Sharers manage active links from a dedicated "Manage Shared Links" panel
under user settings.

Highlights:
- 9 new App Router endpoints under /api/share (create, [token], cover,
  og.png, download, download/confirm, import, revoke, list).
- /s landing page with branded next/og chat unfurl image and SSR auth-cookie
  detection so logged-in recipients see "Add to my library" as the primary
  action without layout shift.
- Server-side R2 byte-copy for /import preserves the project's existing
  invariant that every files.file_key is prefixed with its row's user_id;
  stats / purge / delete / download routes work unchanged. URL-encodes the
  copy source so titles with spaces or '&' don't break the copy.
- Universal 7-day expiry cap, no tier differentiation, no "never". DMCA-risk
  reduction. Picker defaults to 3 days.
- Position-aware shares: "Share current page" toggle (off by default for
  privacy) attaches the sharer's CFI; recipient lands at the same paragraph.
- Per-user 50-share cap, rate limiting via Cache-Control: no-store on
  token-bearing responses, atomic SQL increment for download_count via a
  SECURITY DEFINER function so the public confirm beacon stays safe under
  concurrent fire.
- Soft revocation: presigned download URLs (5-min TTL) cannot be cancelled
  before TTL; documented as accepted v1 behavior.
- token + token_hash hybrid storage: public endpoints look up by hash and
  never select the raw token, so accidental SELECT-* leakage on a public
  route can't expose the bearer credential.
- Mobile / desktop Tauri share via tauri-plugin-sharekit; web falls back to
  navigator.share with a clipboard fallback when no native share method
  exists. Share-sheet dismissal no longer silently copies.

UI:
- New Dialog with a settings-card group: iOS-style segmented duration picker
  + toggle slider for "Share current page", on a single row each.
- Reader top-bar Share button, library context-menu Share entry, manage-
  shares list with cover thumbnails and overflow menu.
- New <SegmentedControl> primitive in src/components for reuse.

Coverage:
- Unit tests for token utils + URL parser (20 new tests, full suite at 3445).
- 31 locales translated for all new strings; en plurals hand-added per the
  project's hand-curated en convention.

DB migration in docker/volumes/db/migrations/002_add_book_shares.sql adds
the book_shares table, RLS policies, and the increment_book_share_download
RPC. Migration is idempotent.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 19:03:35 +02:00
Huang Xin 176e5df771 refactor(settings): move Keep Screen Awake to Behavior > Device (#4027)
Relocate the toggle from the library settings menu to the Behavior settings
panel under the Device section. Add a matching command palette entry so the
setting remains discoverable via search.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 18:44:41 +02:00
Huang Xin 579e950756 fix(rsvp): split em-dash and en-dash compound words (#4026)
Speed-read mode flashed words joined by em-dash (—) or en-dash (–) as
a single unreadable token. Split non-CJK tokens on these dashes,
keeping the dash attached to the preceding word so the punctuation
pause still fires, and treat them as pause-triggering punctuation in
the word display duration.

Closes #4022

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 18:26:03 +02:00
Huang Xin eadb355396 fix(txt): recognize 番外/外传 chapter prefixes, closes #4016 (#4025)
Chinese novels commonly use 番外 (bonus), 番外篇, or 外传 as chapter
headings, optionally combined with 第N章. The previous regex only
matched 第N章 at line start, so lines like "番外 第1章 旗开得胜"
were dropped from the TOC. Treat 番外篇/番外/外传 as preface-style
keywords so they match alongside 楔子/前言/etc.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 18:13:28 +02:00
Huang Xin 8ba052dc81 fix(reader): pure black/white footer in eink mode (#3873) (#4024)
The footer NavigationBar and slide-up panels (Color, Progress, Font &
Layout) used `bg-base-200`, which is computed as a slightly-darker shade
of the theme's background. In eink mode this rendered as a visible grey
strip even when the user picked a pure white/black theme.

Switch to `bg-base-100` in eink mode and add a `border-base-content` top
border so the bar stays visually separated from the page area without
relying on a tinted background, matching the existing eink treatment
elsewhere (e.g. PageNavigationButtons, ImageViewer).
2026-05-01 18:05:36 +02:00
Lex Moulton 293d5b5f5d fix(rsvp): cross-device resume seeding + mobile slider drag (#4004)
* fix(rsvp): seed local position from synced BookConfig on resume

* refactor(rsvp): simplify seedPosition

Consolidate the matched/mismatched write paths into one localStorage
write, extract stripCfiPath() to a module-level helper, and trim the
comments around it.

* fix(rsvp): make progress bar draggable on mobile

Three coordinated fixes so the RSVP overlay's seek bar works reliably
under touch:

- Add `touch-action: none` on the slider so the mobile browser stops
  claiming the gesture for scroll/pan and firing pointercancel mid-drag.
- Hoist the `.rsvp-controls`/`.rsvp-header` exclusion to the top of the
  overlay's touchend handler so a successful drag isn't immediately
  re-interpreted as a speed-change swipe.
- Guard `releasePointerCapture` with `hasPointerCapture` so pointercancel
  arriving after the browser has already released capture (multitouch,
  app backgrounding) no longer throws NotFoundError.
2026-05-01 17:42:58 +02:00
Huang Xin fb37406b31 feat(annotations): preview mode for deep-link landings (#4019)
* feat(annotations): preview mode for deep-link landings

When the reader opens at a deep-link CFI (e.g. clicking an exported
highlight from Obsidian), the position should not be persisted as the
user's reading progress until they actually start reading. Otherwise
the deep-link visit overwrites their last-read position and propagates
that across all sync targets.

Adds a per-book `previewMode` flag in the reader store that:

- Is set to true in FoliateViewer when the URL's `?cfi=` overrides the
  saved last-position.
- Is cleared on the first user-initiated relocate (page turn / scroll),
  reusing the existing reason filter in `docRelocateHandler`.
- Gates the auto progress writers:
    - useProgressAutoSave — skip local config persist
    - useProgressSync     — skip auto-push and skip the remote-progress
                            view.goTo (so cloud pull doesn't yank the
                            user away from the previewed annotation)
    - useKOSync           — skip auto-push (manual pushes still respected)

Hardcover sync and Discord presence are unaffected: hardcover only
fires on explicit user button press, and Discord presence carries no
position information.

Also picks up the regenerated AndroidManifest.xml change from the
existing tauri.conf.json deep-link config (registers readest:// scheme
on Android so the smart landing page's intent:// launch resolves).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(annotations): jump in place when target book is already open

When an annotation deep link arrives while the user is already in the
reader (most common case on mobile App Links), navigateToReader was
pushing the same /reader path with a different cfi query param. The
reader's init useEffect has [] deps, so it doesn't re-run, and
FoliateViewer doesn't re-read the cfi — the view stayed put.

Detect a mounted view for the target book hash by walking
viewStates and matching the hash prefix on the bookKey. If found,
call view.goTo(cfi) directly and set previewMode so the existing
gates fire. Falls back to navigateToReader when no view is open.

Also adds a console.log on each parsed deep link to make this path
easier to debug from device logs in the future.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 17:37:50 +02:00
Huang Xin 486659a1ca feat(annotations): deep links for highlight exports (#4018)
* feat(annotations): deep links for highlight exports

Embed an HTTPS deep link in markdown export so clicking a highlight in
Obsidian / Notion / Mail launches Readest at the exact CFI position.
Mobile App Links / Universal Links open the native app silently when
installed; desktop attempts the readest:// scheme automatically with a
manual fallback.

- Markdown export wraps the page-number text in a per-annotation link:
  https://web.readest.com/o/book/{hash}/annotation/{id}?cfi=...
- New /o/... smart landing page handles platform routing (intent:// on
  Android Chrome, scheme + visibility-cancel on other Android, auto
  scheme + 1 s fallback on desktop, manual button on iOS).
- Reader honors a ?cfi= query param on initial load (overrides the
  saved last-position for the primary book only).
- New useOpenAnnotationLink hook handles incoming readest:// and
  https://web.readest.com/o/... URLs, including cold-start (getCurrent)
  and library-load deferral; supports the legacy flat shape
  readest://annotation/{hash}/{id} from previous Readwise syncs.
- ReadwiseClient now emits the HTTPS deep link instead of the legacy
  custom scheme.
- AASA extended with /o/* matcher; Android intent-filter for the host
  has no pathPrefix so it already covers it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* i18n: translate annotation deep-link strings across all locales

Translates the 13 new keys introduced for the annotation deep-link
feature into all 31 supported locales. Replaces all 403
__STRING_NOT_TRANSLATED__ placeholders.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 07:26:47 +02:00
Huang Xin 5a0a70a30a feat(reader): custom dictionaries (StarDict + MDict) (#4012)
* feat(reader): custom dictionaries (StarDict + MDict)

Adds a pluggable dictionary provider system. Built-in Wiktionary +
Wikipedia (extracted from the legacy single-popup model into a tabbed
shell) plus user-importable StarDict (.ifo/.idx/.dict.dz/.syn) and MDict
(.mdx/.mdd) bundles.

Settings → Language → Dictionaries: import / enable / drag-reorder /
delete (delete-mode toggle mirrors CustomFonts). Drag uses @dnd-kit with
pointer/touch/keyboard sensors. Reader popup: tabbed UI, per-tab lookup
history, scroll-aware back button, last-active tab persists. Tabs grow
to natural width up to a cap, truncate with ellipsis when crowded; phantom
bold layer prevents layout shift on focus.

StarDict reader is self-contained (replaces unused foliate-js/dict.js),
with lazy random-access binary search on .idx + .syn (~420 KB Int32Array
of byte offsets vs ~10 MB of parsed JS objects), lazy DictZip chunk
decompression via fflate streaming Inflate (cmudict/eng-nld both
chunked), and an optional .idx.offsets sidecar generated at import to
skip the init scan. Cmudict 105K-entry init drops from ~10 MB heap and
2 MB IO to ~1.7 MB heap and ~500 KB IO.

MDict uses the readest/js-mdict fork (added as a submodule, consumed via
tsconfig paths so deps stay out of readest's pnpm-lock) which adds a
browser-friendly BlobScanner reading via blob.slice(...).arrayBuffer()
— slices are lazy when the Blob is Readest's NativeFile / RemoteFile.
encrypt=2 (key-info-only) MDX is fully supported via ripemd128-based
mdxDecrypt; encrypt=1 (record-block, needs user passcode) surfaces as
unsupported.

Wikipedia annotation tool removed (Wikipedia is now a tab inside the
unified popup); legacy WiktionaryPopup / WikipediaPopup deleted. Stale
annotationQuickAction === 'wikipedia' coerced to 'dictionary' on settings
load. iOS-friendly external links: skip target="_blank" on Tauri to
avoid the WebView's "open externally" path triggering the shell scope
error; the popup's container click handler routes through openUrl.

i18n: 939 strings translated across 31 locales (30 base keys + CLDR
plural forms for ar/he/sl/pl/ru/uk/ro/it/pt/fr/es).

Test fixtures bundled: cmudict (StarDict, 105K entries), eng-nld
(StarDict, smaller), and a Longman Phrasal Verbs MDX (encrypt=2).
3396 unit tests pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(stardict): resolve fflate from js-mdict source for vitest + Next

js-mdict is consumed as TypeScript source via tsconfig paths from
packages/js-mdict/src/. Its sources `import 'fflate'` directly, but
fflate is only installed under apps/readest-app/node_modules — so
vite's import-analysis (and Next/Turbopack's resolver) can't find
fflate when it walks up from the redirected js-mdict source location.
CI's fresh checkout exposes this; locally a leftover
packages/js-mdict/node_modules/fflate from the old workspace setup
masked it.

Pin fflate resolution to apps/readest-app/node_modules/fflate in:
- vitest.config.mts (Vite alias)
- next.config.mjs (webpack alias + Turbopack resolveAlias — Turbopack
  rejects absolute paths so use a project-relative form)
- tsconfig.json (paths entry so tsgo / Biome see it)

Verified by deleting packages/js-mdict/node_modules locally and
re-running pnpm test (3396 pass), pnpm lint (clean), and both
pnpm build-web and a tauri-platform Next build (clean).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 19:16:36 +02:00
dependabot[bot] a9684ab357 chore(deps): bump amondnet/vercel-action in the github-actions group (#4008)
Bumps the github-actions group with 1 update: [amondnet/vercel-action](https://github.com/amondnet/vercel-action).


Updates `amondnet/vercel-action` from 25 to 42
- [Release notes](https://github.com/amondnet/vercel-action/releases)
- [Changelog](https://github.com/amondnet/vercel-action/blob/master/CHANGELOG.md)
- [Commits](https://github.com/amondnet/vercel-action/compare/v25...v42)

---
updated-dependencies:
- dependency-name: amondnet/vercel-action
  dependency-version: '42'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-30 07:03:44 +02:00
Huang Xin a43845b4c5 fix(layout): symmetric margins and gap in 2-column layout, closes #3909 (#4002) 2026-04-29 20:30:48 +02:00
Huang Xin 1d8ed3fc92 fix(footnote): ignore background image in footnotes (#3998) 2026-04-29 18:01:23 +02:00
Huang Xin d609de58f0 fix(reader): preserve position when toggling scrolled mode, closes #3987 (#3996)
The paginator's scrolled-mode scroll handler is debounced 250 ms, so
#anchor and #primaryIndex can lag behind the user's actual viewport.
Toggling out of scrolled mode within that window made
render() → scrollToAnchor(#anchor) restore the stale anchor, reverting
the position to a previously visible section.

Update foliate-js to flush the pending scroll state before flow leaves
'scrolled', and add regression coverage for the multi-section toggle path.
2026-04-29 09:32:54 +02:00
Huang Xin f9a7117253 fix(sync): defers updateLibrary until libraryLoaded to avoid occasional losing books (#3995) 2026-04-29 07:47:06 +02:00
Huang Xin 234ecc3113 fix(epub): fall back to case-insensitive zip lookups (#3991)
Try an exact ZIP entry match first, then fall back to a case-insensitive lookup for EPUB resources when archive entry casing differs from manifest paths.

Keep ambiguous case-only duplicates exact-match only, and add a regression test that opens a real EPUB through DocumentLoader.
2026-04-28 21:10:10 +02:00
Huang Xin dab92c8a46 fix(pdf): prevent continuous scroll kickback (#3990)
Update foliate-js to preserve the current intra-page scroll anchor when fixed-layout pages relayout or replace placeholders in scrolled mode, instead of snapping back with scrollIntoView().

Add regression coverage for the scroll-anchor restore path.

Closes #3876.
2026-04-28 20:26:55 +02:00
Huang Xin ad55375f89 fix(tts): preserve state on AbortError to keep rate changes effective on iOS, closes #3949 (#3988)
iOS WKWebView's in-flight audio.play() rejects with AbortError after audio.src is reset
during a stop+restart cycle. That rejection leaks through one of the .catch chains into
TTSController.error(), which unconditionally flipped state to 'stopped' — desyncing the
state machine so subsequent rate changes fell into the no-op else branch and #speak's
auto-forward gate stopped firing at paragraph end.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 19:45:05 +02:00
Huang Xin 34f19fd148 fix(annotation): preserve line breaks in selected text across <br> elements, closes #3981 (#3986)
In multi-line PDF selections, pdf.js renders each text run as its own <span>
and inserts <br role="presentation"> at line endings. getTextFromRange only
walked text nodes, so <br>s were dropped and adjacent line-final/line-initial
words glued together (e.g. "lastfirst") in highlights, notes, and AI inputs.

Walk elements alongside text nodes and emit "\n" for <br>, mirroring how
Selection.toString() handles line breaks.
2026-04-28 18:56:55 +02:00
Huang Xin 920627ae59 feat(rsvp): use jieba tokenizer to segment words for Chinese books (#3985) 2026-04-28 18:19:05 +02:00
Huang Xin 4b0720a3e3 perf(rsvp): windowed context, extraction caching and lazy CFI for sections with thousands of words, closes #3953 (#3984)
* perf(rsvp): windowed context, extraction caching and lazy CFI for sections with thousands of words, closes #3953

* i18n: update translations
2026-04-28 17:29:33 +02:00
Huang Xin 3b03b2c8d5 fix(txt): more robust txt parsing, closes #3970 (#3983) 2026-04-28 15:35:35 +02:00
Huang Xin 6fbf9ef68b fix(layout): fixed layout for catalog title (#3982) 2026-04-28 15:34:29 +02:00
Zeedif ca8f0fe9f6 feat(opds): add OPDS-PSE streaming support and custom OPDS 2.0 parser (#3951)
* feat(opds): add OPDS-PSE streaming support and custom OPDS 2.0 parser

* refactor(opds): remove custom parser, use updated foliate-js dependency

* fix(opds): resolve PSE auth at fetch time, drop credentials from book.url

Previously the streaming `pse://` virtual file baked the proxy URL with
the basic auth header into `book.url`, which (a) failed on desktop where
no proxy is used because the auth header was never applied to the page
fetch, and (b) leaked the credential to the sync server because transient
books still get pushed on first sync.

Now the `pse://` payload stores only the upstream OPDS template URL plus
the catalog id. A new `createPseStreamPageLoader` looks up the catalog
from settings on each open, probes auth once (cached for the session),
and applies the auth header via `tauriFetch` on desktop or via the proxy
URL on web.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Huang Xin <chrox.huang@gmail.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 15:14:44 +02:00
Lex Moulton 6d5e59c79a fix(rsvp): resume at stop word, prevent section replay, restore full context (#3960) 2026-04-28 09:02:40 +08:00
Huang Xin 6fcda66b60 fix(reader): close stuck reader window on book load failure, closes #3932 (#3980)
When a book's underlying file is missing, opening it in a dedicated
reader window showed an error toast then navigated that window to
/library, leaving a leftover library-in-reader-window the user had
to close manually. Route the recovery through a new
closeReaderWindowOrGoToLibrary() that closes the dedicated reader
window (after ensuring the main library window is visible) and only
falls back to /library navigation in the main window or on web.

Also fix a related macOS issue: the reader's CloseRequested handler
was running handleCloseBooks and calling currentWindow.destroy() on
the main window, which tore down the active book and bypassed the
Rust close-to-hide handler — making Cmd+W / traffic-light close
quit the app from the reader page (vs. correctly hiding from the
library page) and lose the active book even when the window did
hide. Skip both the cleanup and destroy on macOS for the main
window so the Rust handler hides it with the book intact, matching
the macOS minimize-to-dock convention.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 18:32:04 +02:00
Huang Xin 94ede10f6e fix(annotator): defer Android quick action until touchend, closes #3935 (#3979)
On Android, long-press selects text via selectionchange while the finger
is still on the screen. The quick action handler was gated by
androidTouchEndRef and silently returned, so no popup ever opened. After
the user lifted, nothing re-ran the gated action.

Track the gated action in a small DeferredActionState ref and flush it
from the native touchend handler, so instant copy/dictionary/wikipedia/
search/translate/tts now fire on the first long-press release.
2026-04-27 17:37:22 +02:00
Huang Xin 4f55920b71 feat(kosync): add metadata hash info dialog to diagnose sync failures (#3978) 2026-04-27 17:04:29 +02:00
Huang Xin 63b0b87028 fix(layout): fixed dropdown menu layout for the delete button in details, closes #3940 (#3976) 2026-04-27 15:50:51 +02:00
Huang Xin 17f2a17adc fix(toc): fix auto scroll on book open with pinned sidebar, closes #3945 (#3975) 2026-04-27 15:35:19 +02:00
Huang Xin e18bfd6810 fix(reader): smooth out mouse wheel scrolling in scroll mode, closes #3966 (#3974)
The browser delivers one large quantised delta per wheel notch, which
Chromium scrolls without interpolation — producing the jerky one-step
motion reported on Windows. Detect mouse-wheel-shaped events inside
the iframe (line-mode, or single-axis with |deltaY| ≥ 50), suppress
the native scroll, and replay the delta as an rAF exponential lerp on
the renderer's container. Trackpad / high-resolution input is left to
native scrolling so its momentum and 2-axis behaviour are preserved.
2026-04-27 14:58:43 +02:00
Huang Xin 6d798542f6 fix: restore main library window when going to library from reader, closes #3969 (#3973)
When the main window has been destroyed (Windows/Linux default close), the
reader's "go to library" button only closed the reader, leaving no library
visible. Add ensureMainLibraryWindow() that shows an existing main window
or recreates one with the 'main' label so the existing close-reader-window
wiring keeps working. Also grant the cross-window show/unminimize permissions
the call now needs.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 14:22:28 +02:00
loveheaven ebbbf104b2 feat(cjk): support inline annotation(warichu, Gezhu) layout (#3934)
* feat(warichu): support warichu (割注) inline annotation layout

- Add warichu HTML transformer that converts <span class="warichu"> and
  <warichu> elements into .warichu-pending placeholders during content load
- Implement runtime layout (layoutWarichu / relayoutWarichu) that measures
  column position and splits text into small inline-block chunks (2 chars
  each) so CSS column boundaries can break between them, preventing large
  blank gaps in vertical-rl pagination
- Use column stride (column-width + column-gap) for accurate position
  measurement across column boundaries
- Hook into stabilized event for initial layout and relayout on resize
- Add warichu CSS styles (inline-block chunks, half-size font, vertical align)

* fix(warichu): correct HTML slicing edge cases

- sliceHtml: re-emit tags that were already open before the slice start
  so the result stays well-formed. Previously a slice past an opening
  tag produced an orphan closing tag (e.g. "<b>Hello</b>"[3,5] →
  "lo</b>" instead of "<b>lo</b>").
- sliceHtml / removeFirstVisibleChar / removeLastVisibleChar: treat HTML
  entities (e.g. &amp;) as one visible character so they aren't split or
  truncated mid-entity (e.g. removeFirstVisibleChar("&amp;rest") →
  "amp;rest").
- buildNodes: drop a duplicate chunk.appendChild(l2).
- Add unit tests covering the above for the three pure helpers.

---------

Co-authored-by: Huang Xin <chrox.huang@gmail.com>
2026-04-27 19:12:50 +08:00
Huang Xin fa61f40262 chore: bump opennext and wrangler to the latest versions (#3971) 2026-04-27 10:56:59 +02:00
fabrisnick b251e537d3 chore(deps): update stripe (#3941)
* chore(deps): update stripe

* fix: filter and sort not affecting purchases

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* refactor: rename restoredPurchases to restoredSubscriptions

The filter excludes one-time purchases, so the array contains only
subscriptions. Naming reflects that.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Huang Xin <chrox.huang@gmail.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 10:24:00 +02:00
Huang Xin aa60123d38 fix(rsvp): unicode-aware ORP calculation for non-Latin scripts, closes #3958 (#3964)
JavaScript's `\w` only matches ASCII, so the ORP letter-count regex stripped
all characters from Cyrillic (and other non-Latin) words, producing length 0
and always highlighting the first letter. Use `\p{L}\p{N}_` with the `u` flag
so letters from any script count toward the word length.
2026-04-27 07:00:23 +02:00
JustADeer 1527dd9b3a fix: exponential wheel zoom for images and tables, closes #3956 (#3957)
* Fix ZoomLabel not showing during pinch zoom

* fix: exponential wheel zoom and show ZoomLabel during pinch

- Use exponential wheel zoom (Math.exp) instead of additive for smoother PC scroll
- Show ZoomLabel during pinch start and pinch move
- Clean up unused setZoomSpeed state for wheel handler
2026-04-27 06:17:38 +02:00
Huang Xin 38d7ba80fe feat(opds): support auto-download books from OPDS feeds (#3844)
* feat: auto-download new items from subscribed OPDS catalogs

Add opt-in auto-download per OPDS catalog. When enabled, new publications
are downloaded on app startup and pull-to-refresh. Dedup via Atom <id>.

- Extend foliate-js parser to surface entry id/updated fields
- Add subscription state to OPDSCatalog type
- New headless opdsSyncService with navigation feed crawling
- Auto-download toggle in CatalogManager UI
- Wire sync into startup and pull-to-refresh hooks
- 16 new test cases

Built with an AI code agent.

Closes #3836

* chore: point foliate-js submodule to fork with OPDS id/updated fields

CI needs to fetch the foliate-js commit that adds id and updated field
extraction from OPDS feeds. Point submodule URL to our fork where the
commit is pushed.

* feat(opds): rearchitect auto-download with two-phase OPDS sync

Replaces the monolithic opdsSyncService from #3837 with a separated
discovery + acquisition pipeline. Subscription state lives in
per-catalog JSON files under Data/opds-subscriptions/, decoupled from
catalog config.

Discovery (services/opds/feedChecker.ts) resolves a catalog URL down
to its "by newest" feed via three detection tiers — rel="http://opds-
spec.org/sort/new" (Calibre / Calibre-Web), title heuristics
("Newest", "Recently Added", "Latest"), and href patterns
(?sort_order=release_date for Project Gutenberg, /new-releases for
Standard Ebooks). It then walks only that feed plus rel=next
pagination, never the full navigation tree. When no by-newest feed is
found, the catalog is skipped with a warning instead of silently
mirroring everything.

Acquisition link selection filters to safe rels
(acquisition / acquisition/open-access — drops buy / borrow /
subscribe / sample / indirect), prefers open-access when both rels
are present, then ranks by format tier:
  0  Advanced EPUB / EPUB 3 (link title, .epub3 href, version=3.x)
  1  plain EPUB
  2  MOBI / AZW / AZW3
  3  PDF / CBZ
  4  anything else
Within a tier, ties resolve by feed order. When the upstream omits or
mis-declares (octet-stream) the link's media type, format is inferred
from href extension and link title. Entries that appear in both
feed.publications and a group are deduped within the same batch.

Orchestration (services/opds/autoDownload.ts) runs catalogs
sequentially — per-catalog concurrency already caps parallel downloads
at 3, so a parallel fan-out across catalogs would be N×3 simultaneous
requests on cellular. Pending and retry items are deduped by entryId,
and entries still inside their exponential-backoff window are skipped
instead of re-attempted (avoids appending duplicate failedEntries
records). Filenames come from the last path segment, capped at 200
chars, with a try/catch around decodeURIComponent for malformed
%-sequences.

Hook (hooks/useOPDSSubscriptions.ts): startup, 5-minute interval, and
'check-opds-subscriptions' event triggers. Toggling auto-download on
fires the event so the sync runs immediately. Newly imported books
are queued for cloud upload via transferManager when the user is
logged in and settings.autoUpload is on, mirroring the manual OPDS
download path. 'opds-sync-complete' is dispatched after every sync so
listening UI can refresh without polling. loadSubscriptionState
self-heals duplicate failedEntries from older state files.

The submodule URL is reverted to readest/foliate-js (the changes
needed for OPDS id/updated extraction are already in main), and
OPDSCatalog is trimmed to only the autoDownload field.

Verbose [OPDS]-prefixed debug logs cover the whole pipeline: feed
fetch → resolve → discovery → per-item download (with magic-byte
dump of the saved file) → import → cloud upload queueing.

34 new tests across opds-feed-checker, opds-auto-download,
opds-subscription-state, opds-types.

Closes #3836.
Supersedes #3837.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Johannes Mauerer <johannes@mauerer.info>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 06:04:32 +02:00
Archisman Panigrahi 8a8fcb2611 chore: add VCS browser URL to appdata.xml (#3962)
This is now recommended in flatpak. See warning in https://github.com/flathub/com.bilingify.readest/pull/20#issuecomment-4322583486
2026-04-27 09:34:30 +08:00
Huang Xin d488f65447 fix(seo): fixed the title and description of the user page (#3952) 2026-04-25 09:56:13 +02:00
Huang Xin 528a13e36a fix(layout): don't dismiss notebook on navigate to annotations when notebook is pinned, closes #3923 (#3948) 2026-04-24 17:42:17 +02:00
Huang Xin 71371130e0 fix(android): avoid rsproperties panic on startup, closes #3922 (#3942)
`rsproperties::get` panics when it can't open or parse the
`/dev/__properties__` layout (documented behavior of the crate).
On some older/unusual Android builds (e.g. MediaTek Android 8.1 on
Xiaomi Mipad), this aborts the app with SIGABRT before the main
window is created.

Replace the crate with a direct FFI call to Android's native
`__system_property_get`, which has existed since the earliest
Android versions and returns an error code instead of panicking.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 06:00:31 +02:00
Huang Xin be8b946683 chore: update Dependabot dependencies (#3938)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-23 12:27:39 +02:00
dependabot[bot] 82ece3332a chore(deps): bump mozilla-actions/sccache-action (#3937) 2026-04-23 12:38:45 +08:00
Huang Xin 957b7d5f3e fix(layout): properly display full screen page, closes #3914 (#3930) 2026-04-22 15:05:47 +02:00
Huang Xin 5190bbcafc fix(macos): don't quit app on Cmd+W, only on Cmd+Q, closes #3927 (#3928)
On macOS, closing the last window (Cmd+W or the red traffic light) was
quitting the app, which is unexpected — the native convention is that
Cmd+W closes the window while the app keeps running in the dock, and
only Cmd+Q quits.

Intercept the window CloseRequested event on macOS, prevent the close,
and hide the window instead so the app remains active. Handle the
Reopen event (fired when the user clicks the dock icon) to restore the
hidden window. Cmd+Q continues to go through applicationWillTerminate:
and exits the app normally.
2026-04-22 14:11:04 +02:00
Huang Xin 3f531d9046 fix(theme): fixed texture background in scrolled mode, closes #3913 (#3918) 2026-04-21 15:44:59 +02:00
Huang Xin a2244e28b8 fix(pdf): don't apply theme colors where canvas filter is unsupported, closes #3912 (#3915) 2026-04-21 10:58:26 +02:00
Huang Xin 3bbc2071c8 fix(pdf): fixed annotations not displayed properly in two-page spread for PDFs, closes #3862 (#3906) 2026-04-20 18:41:32 +02:00
Huang Xin 9c273d79fc fix(tts): fixed race condition on pause/resume, closes #3825 (#3905) 2026-04-20 17:57:20 +02:00
Huang Xin 09b19bd3c5 perf(rsvp): fixed performance issue when the context window is large, closes #3877 (#3904) 2026-04-20 17:25:48 +02:00
Huang Xin c58153e942 compat(css): remove no-op css that might break column layout, closes #3895 (#3903) 2026-04-20 16:28:10 +02:00
Huang Xin ff94dc76c6 fix: fixed crash on app start when there is no main window but a reader window running, closes #3897 (#3902) 2026-04-20 16:05:01 +02:00
Huang Xin 293cc545db fix(kosync): send valid progress to kosync server when closing book, closes #3899 (#3901) 2026-04-20 15:16:25 +02:00
Huang Xin e1dad98e56 fix(toc): prevent auto-scroll snap-back on sidebar open (#3900)
The TOC occasionally flashed a scroll to the current item and then
snapped back to the top, and on slow mobile first-opens sometimes
stayed at the top entirely.

Root cause: `useOverlayScrollbars({ defer: true })` schedules OS
construction via `requestIdleCallback` with a ~2233 ms timeout. On a
busy first open the timeout fires before the browser goes idle, so OS
wraps the viewport late — and the wrap step resets the scroller's
`scrollTop` synchronously, undoing Virtuoso's earlier scroll to the
current item. Virtuoso's `rangeChanged` / `onScroll` don't propagate
the reset for another frame, so any guard based on tracked scroll
state reads stale.
2026-04-20 14:07:49 +02:00
Zach Bean e8f6db96e4 fix(kosync): CWA sometimes sends 400 for auth failure (#3893) 2026-04-18 17:55:33 +02:00
Huang Xin b223ccaee9 feat(footnotes): detect more formats of footnote (#3894) 2026-04-18 17:55:06 +02:00
Zach Bean 5c97b2e9d8 feat(kosync): defer push after resume (#3892)
* feat(kosync): defer push after resume

* PR feedback updates:
 * follow established patterns from other hooks
 * error handling
 * fix typos
2026-04-18 17:28:20 +02:00
Huang Xin 31e44d2e4d fix(a11y): fixed saving reading progress with screen readers, closes #3864 (#3891) 2026-04-18 14:52:40 +02:00
Huang Xin 976bbcc152 fix(library): fixed opening shared books from other apps (#3884) 2026-04-16 14:46:31 +02:00
Huang Xin 802212c423 refactor: fixed typo in module name (#3881) 2026-04-16 07:43:04 +02:00
dependabot[bot] e858f9b23f chore(deps): bump the github-actions group with 2 updates (#3880)
Bumps the github-actions group with 2 updates: [pnpm/action-setup](https://github.com/pnpm/action-setup) and [actions/github-script](https://github.com/actions/github-script).


Updates `pnpm/action-setup` from 5 to 6
- [Release notes](https://github.com/pnpm/action-setup/releases)
- [Commits](https://github.com/pnpm/action-setup/compare/v5...v6)

Updates `actions/github-script` from 8 to 9
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](https://github.com/actions/github-script/compare/v8...v9)

---
updated-dependencies:
- dependency-name: pnpm/action-setup
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: actions/github-script
  dependency-version: '9'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-16 06:53:06 +02:00
Huang Xin 3e292af990 refactor(nav): refactor book nav service with TOC enrichment (#3874) 2026-04-15 17:08:17 +02:00
Huang Xin b0cc5461af refactor(toc): cache navigable structure per book (#3869)
* refactor(toc): cache TOC + section fragments per book

Moves the TOC regrouping and section-fragment computation out of
foliate-js/epub.js #updateSubItems into the readest client as
computeBookNav / hydrateBookNav in utils/toc.ts. The result is
persisted to Books/{hash}/nav.json — capturing the book's full
navigable structure (TOC hierarchy + sections with hierarchical
fragments). Compute once, persist locally, hydrate on subsequent
opens. Designed to serve current human-facing navigation (TOC
sidebar, progress math) and future agentic navigation (LLM-driven
seeking by structural location).

Versioned by BOOK_NAV_VERSION for forward invalidation. Existing
books regenerate transparently on next open.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: update worktree scripts

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 20:15:10 +02:00
Yuan Tong 7d852518a3 feat(windows): use overlay scrollbar (#3868)
* feat(windows): use overlay scrollbar

* fix format
2026-04-14 19:03:07 +02:00
Huang Xin 73d30c103f fix(toc): fix page number of some TOC items from section fragments (#3867) 2026-04-14 16:22:06 +02:00
Huang Xin 8cdf378b47 fix(i18n): fix translations in RU (#3866) 2026-04-14 08:54:05 +02:00
Huang Xin 1088af023b release: version 0.10.6 (#3861) 2026-04-13 12:55:13 +02:00
Huang Xin 011ad18a02 fix(android): use stable safe area insets to avoid unnecessary layout shift, closes #3670 (#3859) 2026-04-13 12:04:41 +02:00
Huang Xin e9d71b2936 feat(settings): add an option to avoid overriding paragraph layout, closes #3824 (#3858) 2026-04-13 09:42:53 +02:00
Huang Xin ec32614539 fix(settings): fixed color picker for custom highlight colors, closes #3796 (#3857) 2026-04-13 08:25:48 +02:00
Huang Xin 96678d85ec refactor(settings): persist the apply-globally toggle per book (#3856) 2026-04-13 07:45:24 +02:00
Huang Xin 41b5e92563 feat(annotator): support instant copy operation for selected text, closes #3828 (#3854) 2026-04-13 06:00:05 +02:00
Huang Xin ef97a8ed02 fix(ux): optimize scrolling UX for the bookshelf and sidebar content (#3849) 2026-04-12 20:52:12 +02:00
Huang Xin 8df8bc8b4a chore(agent): use claude in chrome for web based qa (#3847) 2026-04-12 12:26:20 +02:00
DrSheppard f0e23a1503 fix(linux): update package installation for Linux-x64 (#3845) 2026-04-12 11:01:00 +02:00
Huang Xin 4e1464ef17 fix(macOS): don't show window button when traffic lights are on the header, closes #3831 (#3843) 2026-04-12 10:05:27 +02:00
Huang Xin 7b60b1bb0c fix(ios): reduce GPU memory pressure to prevent WebKit GPU process crash (#3842)
On iOS, navigating to a book group in the library caused the WebKit GPU
process to exceed its 300 MB jetsam limit (peaking at ~328 MB), resulting
in a blank screen flash and broken scroll state.

Three changes reduce peak GPU memory usage:

- Add overscan={200} to VirtuosoGrid/Virtuoso so only items within 200px
  of the viewport are rendered, limiting simultaneous image decoding
- Add loading="lazy" to both Image components in BookCover so the browser
  defers decoding offscreen cover images
- Conditionally mount the <video> and <audio> elements in AtmosphereOverlay
  only when atmosphere mode is active, eliminating idle H.264 decoder
  memory overhead

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 09:06:16 +02:00
Huang Xin cc780712b9 fix(deps): add pnpm override for qs >=6.14.2 (Dependabot #71) (#3841)
Fixes DoS vulnerability from arrayLimit bypass in comma parsing.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 06:58:31 +02:00
Huang Xin 95ff526140 fix(deps): bump dependencies to resolve 13 Dependabot security alerts (#3840)
Update next (16.2.3), vite (7.3.2+), react/react-dom (19.2.5),
@vitejs/plugin-rsc (0.5.23), react-server-dom-webpack (19.2.5),
and add overrides for lodash (4.18.0), lodash-es (4.18.0),
basic-ftp (5.2.2) to fix high/medium severity vulnerabilities
including DoS, code injection, prototype pollution, CRLF injection,
arbitrary file read, and path traversal.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 06:44:30 +02:00
Huang Xin f86bbbcc22 perf(library): virtualize grid and list of book items when rendering library page (#3835) 2026-04-11 21:25:06 +02:00
Huang Xin 20940105fb feat(library): navigate to previous group with the Back button on Android, closes #2675 (#3833) 2026-04-11 20:08:00 +02:00
Huang Xin 2a49e93cf7 fix(library): fixed the All button in groups breadcrumbs navigation bar, closes #3782 (#3832) 2026-04-11 19:55:08 +02:00
Lex Moulton 030a7c0823 perf: optimize library operations for large collections (#3827)
* perf(store): decouple page turn from full library rewrite for large collections

Previously every page turn triggered setLibrary() which copied the entire
library array, ran refreshGroups() with MD5 hashing over all books, and
caused cascading re-renders. With ~2800 books this made reading unusable.

- Add hash-indexed Map to libraryStore for O(1) book lookups
- Add lightweight updateBookProgress() that skips array copy and refreshGroups
- Use hash index in setProgress, saveConfig, and initViewState
- Batch cover URL generation with concurrency limit on library load

Addresses #3714

* perf(import): replace filter()[0] with find() to short-circuit on first match

* fix(store): replace Object.assign state mutation with immutable spread in setConfig

* perf(persistence): remove JSON pretty-printing to reduce serialization overhead

* fix(reader): stabilize debounce reference in useIframeEvents to prevent timer reset on re-render

* perf(context): memoize provider values to prevent unnecessary consumer re-renders

* perf(store): cache visible library to avoid refiltering on every access

* perf(library): remove redundant refreshGroups call already triggered by setLibrary

* perf(import): replace O(n) splice(0,0) with O(1) push for new book insertion

* perf(import): defer library persistence to end of import batch instead of every 4 books

* perf(library): skip full library reload on reader close since store is already in sync

* fix: address PR review feedback for library perf optimizations

Correctness fixes for issues found in code review:

- fix(library): restore library reload on close-reader-window. Reader
  windows are independent Tauri webviews with their own libraryStore
  instance, so progress / readingStatus / move-to-front updates from
  the reader do not propagate to the main window. Reload from disk
  so the library reflects the changes the reader just persisted.

- perf(import): wire BookLookupIndex into importBooks. The lookupIndex
  parameter on bookService.importBook had no caller, leaving the
  Map-based dedup path dead. Build the index once per import batch
  in app/library/page.tsx and thread it through appService.importBook
  so the O(1) dedup path is actually exercised.

- perf(import): defer library save to end of batch. Add a skipSave
  option to libraryStore.updateBooks and call appService.saveLibraryBooks
  once after the entire import loop, instead of once per concurrency-4
  sub-batch.

- fix(store): make updateBookProgress immutable. The previous in-place
  mutation reused the same library array reference, bypassing Zustand
  change detection AND leaving the visibleLibrary cache holding stale
  Book references. Now slice the array, update the entry, and refresh
  visibleLibrary. Also make readingStatus a required parameter so
  future callers cannot accidentally clear it by omitting the argument.

- fix(store): make saveConfig immutable. It previously mutated the Book
  object's progress / timestamps in place and used splice/unshift on
  the shared library array. Now spread to a new book object and rebuild
  via setLibrary. Also corrects the interface signature to return
  Promise<void> (the implementation was already async).

- fix(store): make updateBook immutable for the same reason — it was
  mutating the previous-state library array before spreading.

- fix(context): wrap AuthContext login/logout/refresh in useCallback.
  Without this, the useMemo deps array changed every render and the
  memo was a no-op, defeating the optimization the PR was trying to
  add.

- fix(reader): use a ref for handlePageFlip in useMouseEvent's debounce.
  The empty-deps useMemo froze the first-render handler; with the ref
  the debounced wrapper always invokes the latest closure.

Test coverage added:
- library-store: immutable updateBookProgress, visibleLibrary cache
  refresh, deleted-book filtering, updateBooks skipSave option
- book-data-store: immutable saveConfig, move-to-front correctness,
  visibleLibrary order, persistence behavior
- import-metahash: BookLookupIndex update on new import, lookup-index
  consultation before scanning books array
- auth-context (new file): context value identity stability across
  re-renders, callback identity stability
- useIframeEvents (new file): debounced wheel handler dispatches to
  the latest handlePageFlip after re-render

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor(types): move BookLookupIndex to types/book.ts

Avoids the inline `import('@/services/bookService').BookLookupIndex`
type annotation in types/system.ts. Both the AppService interface and
the bookService implementation now import BookLookupIndex from the
canonical location alongside Book.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor(import): convert importBook params to options object

Replace the long positional-argument list on appService.importBook
(saveBook, saveCover, overwrite, transient, lookupIndex) with a
single options object so callers no longer need to pad with
`undefined, undefined, undefined, undefined` to reach the parameter
they actually want to set.

Before:
  await appService.importBook(file, library, undefined, undefined,
                              undefined, undefined, lookupIndex);

After:
  await appService.importBook(file, library, { lookupIndex });

The underlying bookService.importBook is also refactored to take an
options object: required AppService callbacks (saveBookConfig,
generateCoverImageUrl) are bundled with the optional flags via an
ImportBookInternalOptions interface that extends the public
ImportBookOptions defined in types/book.ts.

All existing call sites updated to the new shape.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Huang Xin <chrox.huang@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 16:32:35 +02:00
Huang Xin 7bf4822b27 fix(library): restore breadcrumb 'All' navigation by bypassing next-view-transitions, closes #3782 (#3829)
The breadcrumb "All" button was broken on first click after entering a
group because next-view-transitions@0.3.5's useTransitionRouter wraps
router.replace() in startTransition + document.startViewTransition, and
this combination is incompatible with Next.js 16.2 RSC navigation when
only the search params change for the same pathname (e.g.
/library?group=foo -> /library). The navigation silently never commits.

Extract the library navigation logic into a useLibraryNavigation hook
that uses plain useRouter from next/navigation. The data-nav-direction
attribute is still set so existing directional CSS keeps working when
view transitions fire via popstate.

See https://github.com/shuding/next-view-transitions/issues/65 for the
upstream incompatibility.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 16:04:37 +02:00
Huang Xin de11511c30 fix(layout): fixed bleed layout of images (#3823) 2026-04-10 19:33:56 +02:00
Huang Xin ab7da981da fix(eink): remove scroll animation in eink mode and optimize eink detection (#3822)
* fix(eink): remove scroll animation in eink mode

* fix(android): fix startup ANR on e-ink devices from getprop subprocesses
2026-04-10 18:22:06 +02:00
Huang Xin 3df75a67f9 feat(tts): support edge tts on cloudflare worker (#3819) 2026-04-10 15:32:06 +02:00
Huang Xin 07e3248780 fix: apply disable click to paginate also for non-iframe clicks (#3818) 2026-04-10 12:27:17 +02:00
Lex Moulton 23d5f3363d fix(rtl): fix page navigation for Arabic books (#3817)
* fix(rtl): fix page navigation for Arabic books

* fix(rtl): unified navigation handlers for rtl and ltr

---------

Co-authored-by: Huang Xin <chrox.huang@gmail.com>
2026-04-10 11:54:30 +02:00
Zeedif c6daf72da9 feat(opds): allow editing of registered catalogs (#3814)
* feat(opds): allow editing of registered catalogs

* chore(i18n): add translations for catalog edit feature

Translate "Remove", "Edit OPDS Catalog", and "Save Changes" across all
31 locales.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Huang Xin <chrox.huang@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 06:09:30 +02:00
Zeedif bd866cb049 fix(opds): harden Content-Disposition filename parsing for complex names and encoding (#3816) 2026-04-10 06:08:57 +02:00
Huang Xin a5690e9a84 fix(tts): skip br elements in PDF text layer to prevent TTS interruptions at line breaks, closes #3771 (#3811)
PDF.js TextLayer renders <br> between text spans for visual line wrapping.
The TTS SSML generator was converting these to <break> elements, causing
TTS engines to pause at every PDF line break within paragraphs. Fix by
rejecting <br> (along with canvas and annotationLayer) via the node filter
when the document is detected as a PDF.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 20:34:38 +02:00
Huang Xin ed7cfc31fc fix(layout): fix off-by-one page count on fractional DPR devices, closes #3787 (#3813)
The `pages` getter used Math.ceil(viewSize / containerSize) which inflates
the count by 1 when floating-point drift makes the ratio slightly above an
integer (e.g. 4.00000001 → 5). Use Math.round to absorb sub-pixel drift,
matching the approach already used in #getPagesBeforeView.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 20:08:55 +02:00
Huang Xin a07bf23e18 chore(docs): add worktree management for isolated PR review and feature work (#3810)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 19:42:47 +02:00
Zeedif 41d014914b fix(opds): handle spaces and quotes in Content-Disposition filename parsing (#3812) 2026-04-09 19:42:29 +02:00
Lex Moulton baee85e7c8 feat(rsvp): sync reading position to cloud via book_configs (#3801) 2026-04-09 15:52:49 +02:00
Huang Xin 1e259e87b2 refactor(reader): introduce priority-based touch interceptor for gesture handling (#3809)
Replace the inline touch-swipe event dispatching with a module-level
interceptor registry. This lets the reading ruler (priority 10) claim
drag gestures before the swipe-to-flip handler (priority 0), preventing
conflicts when dragging the ruler on touch devices.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 11:48:21 +02:00
Huang Xin 4abbc17f66 fix(annotator): fixed instant annotation in scrolled mode, closes #3769 (#3808) 2026-04-09 10:34:28 +02:00
Huang Xin d7fd06ca82 chore: add explicit permissions to GitHub Actions workflows (#3807)
Fixes code scanning alerts #1, #2, #3, #4
(actions/missing-workflow-permissions).

- retry-workflow.yml rerun job: actions: write (to rerun workflows)
- upload-to-r2.yml upload-to-r2 job: contents: read (to download releases)
- upload-to-r2.yml retry-on-failure job: actions: write (to trigger retry)
- release.yml upload-to-r2 job: contents: read, actions: write

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-09 07:33:25 +02:00
Huang Xin e43e533aca security: fix complete multi-character sanitization for HTML comments in txt.ts (#3806)
* security: fix for code scanning alert no. 11: Incomplete multi-character sanitization

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>

* fix: use dotAll flag to match multi-line HTML comments

Add the 's' flag to the comment-stripping regex so '.' matches
newlines, ensuring comments spanning multiple lines are also removed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix: iterative dotAll sanitization in extractChaptersFromSegment

Fixes code scanning alert #10 (incomplete multi-character sanitization).
Apply the same fix as alert #11: replace one-shot comment stripping
with an iterative loop using the 's' (dotAll) flag so nested and
multi-line HTML comments are fully removed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix: iterative HTML tag sanitization in cleanDescription

Fixes code scanning alert #9 (incomplete multi-character sanitization).
Replace one-shot tag stripping with an iterative loop so crafted inputs
like nested/overlapping tags cannot leave '<script' behind after a single
replacement pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-09 07:20:56 +02:00
Huang Xin dc788283ad security: fix for code scanning alert no. 11: Incomplete multi-character sanitization (#3804)
* security: fix for code scanning alert no. 11: Incomplete multi-character sanitization

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>

* fix: use dotAll flag to match multi-line HTML comments

Add the 's' flag to the comment-stripping regex so '.' matches
newlines, ensuring comments spanning multiple lines are also removed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-09 06:54:20 +02:00
dependabot[bot] 373420bb0c chore(deps): bump actions/checkout in the github-actions group (#3805)
Bumps the github-actions group with 1 update: [actions/checkout](https://github.com/actions/checkout).


Updates `actions/checkout` from 4 to 6
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-09 06:47:57 +02:00
Huang Xin 6072b0dcbd security: fix for code scanning alert no. 12: Use of externally-controlled format string (#3803)
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2026-04-09 06:23:08 +02:00
Huang Xin 13ff96db85 security: potential fix for code scanning alert no. 19: DOM text reinterpreted as HTML (#3802)
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2026-04-09 06:10:49 +02:00
Lex Moulton bfbe92f355 refactor(sidebar): replace react-window and OverlayScrollbars with react-virtuoso and CSS scrollbars (#3798)
* feat: Add option to split words in RSVP mode

* fix(rsvp): replace lookbehind regex with lookahead-only split in getHyphenParts

* feat: Add option to split words in RSVP mode

* fix(theme): update data-theme and themeCode when system theme changes

* feat(rsvp): split on ellipsis between letters and preserve delimiter type

* fix(rsvp): fix incorrect merged line

* feat(rsvp): insert blank frame between consecutive identical words

* refactor(sidebar): replace react-window and OverlayScrollbars with react-virtuoso and CSS scrollbars

* feat(toc): smooth scroll to active chapter on sidebar open

* test(theme-store): expand dark theme palette fixture with full color tokens

* refactor: remove dead code and consolidate duplicate CSS scrollbar rules

* fix(toc): fix auto-scroll on sidebar open and improve scroll behavior

- Add isSideBarVisible to scroll effect deps so it fires on sidebar open
- Use setTimeout delay for Virtuoso to finish layout before scrolling
- Add 10s cooldown on user scroll to re-enable auto-scroll
- Use smooth scroll for short distances (<16 items), instant for longer
- Track visible center via rangeChanged for accurate distance calculation
- Fix tab navigation background opacity (bg-base-200 instead of /20)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Huang Xin <chrox.huang@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 16:44:45 +02:00
Huang Xin 799db40763 fix(pdf): add an option to apply theme colors to PDF, closes #3778 (#3799) 2026-04-08 07:29:44 +02:00
Huang Xin 932c82aa49 chore(security): update CodeQL workflow to remove languages (#3794)
* chore(security): update CodeQL workflow to remove languages

Removed unsupported languages from CodeQL workflow.

* style: format codeql.yml with Prettier

Fix CI format check failure by applying Prettier formatting to the
CodeQL workflow file.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 21:36:46 +02:00
Huang Xin 184de9210d fix(security): prevent SSRF in kosync proxy (CWE-918) (#3793)
Validate serverUrl in the kosync API proxy to block requests to
private/internal addresses and non-http(s) schemes. Also fix isLanAddress
to detect 0.0.0.0 and bracket-wrapped IPv6 private addresses.

Closes code-scanning alert #14.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 21:21:51 +02:00
Zach Bean 50a2957e35 feat(kosync): support HTTP Basic auth for CWA KOSync servers (#3792)
* Support HTTP Basic auth for kosync connections

* fix(kosync): use separate password field for HTTP Basic auth

- Add `password?` field to KOSyncSettings to store the plain password
  alongside the existing `userkey` (md5 hash), preserving backward
  compatibility for existing users
- Replace Buffer.from() with btoa() for browser-compatible Base64 encoding
- Simplify buildHeaders() to use config fields directly instead of
  the useAuth union type

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Huang Xin <chrox.huang@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 21:07:01 +02:00
Zach Bean 637b7462c4 fix(kosync): use Fragment keys to prevent form field issues (#3791)
* Use key attrs to prevent form field issues

Fixes #3790.

* PR feedback: use Fragment keys instead of individual element keys
2026-04-07 21:05:14 +02:00
Huang Xin 82deb85c64 docs: add threat model and incident response plan to SECURITY.md (#3788)
- Add Threat Model section covering assets, threat actors, attack
  surfaces, and mitigations for ebook parsing, cloud sync, OPDS
  integration, rendered HTML/JS, supply chain, and Tauri IPC
- Add Incident Response Plan with triage, containment, remediation,
  disclosure, and post-incident review steps
- Add severity definitions table

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-07 19:17:56 +02:00
Huang Xin db35a4e203 fix(style): clamp oversized hardcoded pixel widths and fix browser test flakiness (#3785)
Closes #3770.

Add transformStylesheet rule that clips CSS width declarations exceeding the
viewport with max-width and border-box sizing. Also add @testing-library/react
to vitest browser optimizeDeps.include to prevent mid-test Vite reloads on CI.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 07:23:30 +02:00
pythontyphon 017a9338b3 fix(dictionary): add Chinese dictionary lookup with pinyin support (#3784)
The Wiktionary REST API does not support Chinese entries — simplified
characters return 404, traditional characters omit Chinese results, and
no pronunciation data is ever included. Switch Chinese lookups to the
Wiktionary Action API which returns full wikitext with pinyin and
definitions. Also add encodeURIComponent to the REST API URL for all
other languages.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-07 06:16:26 +02:00
AK Venugopal cf21a752c6 Updating Hardcover progress sync logic (Issue #3775) (#3777)
* feat(hardcover): add one-way Hardcover sync integration

- Add HardcoverClient with ISBN/title-based book lookup, progress push, and note sync
- Add HardcoverSyncMapStore for persistent local mapping of note IDs to Hardcover journal IDs
- Add GraphQL queries/mutations for Hardcover API (insert/update/recreate journal entries)
- Add DB migration for hardcover_note_mappings table (schema: hardcover-sync)
- Add HardcoverSettings dialog component (connect/disconnect/enable toggle)
- Add useHardcoverSync hook wired in Annotator for push-notes and push-progress events
- Add Hardcover Sync section in BookMenu with per-book toggle (persisted in BookConfig)
- Add HardcoverSettings type and DEFAULT_HARDCOVER_SETTINGS to system settings
- Add hardcoverSyncEnabled per-book flag to BookConfig
- Mount HardcoverSettingsWindow in Reader alongside KOSync and Readwise windows

Sync is one-way (Readest → Hardcover), manual-action only, per-book opt-in.
Supports idempotent note sync: insert new, skip unchanged, update changed, recreate on stale remote ID.

* fix(hardcover): proxy API calls server-side to fix CORS, normalize Bearer token

- Add /api/hardcover/graphql Next.js route that proxies POST requests server-side,
  bypassing CORS restrictions (Hardcover API has no Access-Control-Allow-Origin header)
- On Tauri (desktop), calls Hardcover directly; on web, routes through the proxy
- Normalize token in HardcoverClient: accept raw JWT or 'Bearer <jwt>' format
- Update helper text to point to hardcover.app → Settings → API

* fix(hardcover): surface note-sync no-ops and harden book resolution

- Show toast feedback when book data is still loading, Hardcover is not configured,
  or the current book has no annotations/excerpts to sync
- Show an explicit info toast when note sync finds no new changes
- Parse Hardcover search results in the current hits/document response shape
- Resolve note sync through ensureBookInLibrary for parity with progress sync
- Add console logging for note/progress sync failures

* debug(hardcover): add runtime instrumentation for note sync

* feat(metadata): keep identifier stable and store ISBN separately

- Add dedicated metadata.isbn field
- Expose ISBN as its own editable field in book metadata
- Preserve identifier semantics for existing source IDs and hashes
- Route metadata auto-retrieval ISBN handling through the new field
- Prefer metadata.isbn for Hardcover matching

* fix(hardcover): avoid wasm sqlite for note mappings on web

- Store Hardcover note sync mappings in localStorage on web
- Keep sqlite-backed mappings for desktop/native environments
- Remove the web-only database dependency from manual note sync

* fix(hardcover): dedupe notes by payload hash across unstable note IDs

- Add payload-hash lookup in HardcoverSyncMapStore
- Reuse existing journal mapping when payload already synced
- Prevent duplicate insertions when note IDs change or duplicate locally

* fix(hardcover): avoid duplicate quote export when annotation note exists

- Detect excerpt+annotation pairs for the same highlight
- Skip standalone excerpt export when annotation has note text
- Keep annotation export as the single source of truth

* docs(hardcover): add consolidated change summary for review

* fix(hardcover): suppress excerpt export by CFI when annotation note exists

* fix(hardcover): suppress empty-note annotation duplicates when note exists

* fix(hardcover): deduplicate notes by text and cfi base node

* refactor(hardcover): optimize sync performance, add rate limiting and clean up debug tools

* chore: remove dev-only change log from main

* test(hardcover): add unit tests for sync mapping and client logic

* chore: custom deployment and UI fixes

* fix(hardcover): use timestamptz for accurate annotation time

* fix(hardcover): use date scalar and RFC 3339 formatting for journal entries

* Revert "chore: custom deployment and UI fixes"

This reverts commit 0329aba7129d1e1ebf2c663804b8fba9a9f87b91.

* Fix hardcover progress dates and surface imported ISBNs

* Fix Hardcover currently reading sync

* fix(hardcover): avoid promoting note sync status

* style(hardcover): apply prettier formatting

* test(hardcover): fix strict TypeScript assertions

* test(hardcover): harden sync regression coverage

* test(hardcover): fix lint and formatting regressions

* fix(hardcover): narrow note dedupe range matching

* refactor(hardcover): extract note dedupe helpers

* refactor(isbn): extract metadata normalization helpers

* feat(hardcover): improve synced quote formatting

* feat(hardcover): sync progress by edition percentage

* fix(hardcover): reuse active read on first sync

* style(hardcover): apply formatting fixes

* fix(hardcover): address adversarial review findings

- null-guard insert_user_book response: throw with error message
  when the API returns a null user_book instead of crashing on
  property access
- skip progress push when Hardcover edition page count is unknown:
  getHardcoverProgressPages now returns null when context.pages and
  context.bookPages are both null (title-search path); pushProgress
  exits early instead of silently sending an inaccurate local page
  number
- apply edition preference for title-search books: after
  searchBookByTitle resolves a book ID, fetch QUERY_GET_BOOK_USER_DATA
  via editions(where: { book_id: ... }) to retrieve the user's active
  read edition and user_book, then apply the same active-read-edition
  -> user_book-edition waterfall used for ISBN lookups
- add regression tests for all three fixes
- verify all query/mutation field names against hardcover schema.graphql
2026-04-06 17:59:51 +02:00
Mohammed Efaz 16adf11258 fix(library): align grid hover highlight corner radius (#3774) 2026-04-06 17:24:48 +02:00
Huang Xin ae2c421938 fix(ui): restore highlight options layout and clean up color name editing (#3776)
* fix(ui): restore highlight options layout and clean up color name editing

Restore the pre-#3741 layout for both the AnnotationPopup highlight
options row and the HighlightColorsEditor settings panel.

HighlightOptions: re-add `justify-between` on the outer container and
remove `flex-1` from the color strip so the gap between the style box
and color strip responds to the number of colors.

HighlightColorsEditor: restore `grid-cols-3 sm:grid-cols-5` grid,
remove always-visible name inputs, and add a click-to-edit popover on
each color circle with hover tooltip for the label.

Add a browser screenshot test that renders the real AnnotationPopup
component with Tailwind CSS and compares against baseline PNGs for
5, 10, and 15 colors.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 16:48:12 +02:00
Huang Xin 3174e341a3 release: version 0.10.4 (#3767) 2026-04-05 19:57:06 +02:00
Huang Xin 298d4872a0 fix(translate): disable yandex provider while upstream relay is down (#3765)
The translate.toil.cc relay that backs the yandex provider is currently
unavailable. Introduce a `disabled` flag on TranslationProvider and
filter disabled providers out of both `getTranslators()` and
`getTranslator()` so the settings panel, translator popup, and the
fallback logic in `useTranslator` all agree the provider doesn't exist —
no per-callsite changes needed. Flip the flag back (or delete the line)
on `yandexProvider` to re-enable once the upstream is healthy.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 19:48:25 +02:00
Huang Xin b679817fce fix(tts): prevent double playback on rapid TTS icon clicks (#3764)
Guard handleTTSSpeak with a single-flight ref so a second tts-speak
event that arrives while the first is still inside its initial awaits
(initMediaSession / backgroundAudio / TTSController.init) is ignored
instead of racing ahead to construct a second TTSController. Without
this, rapid clicks produced two concurrent speakers talking over each
other because viewState.ttsEnabled is only set at the end of the first
invocation, so the footer bar would dispatch tts-speak twice.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 19:11:28 +02:00
Huang Xin b9a2b10fac fix(a11y): fixed keyboard activation of dropdown menu (#3762) 2026-04-05 18:48:27 +02:00
Huang Xin a9e33ca50a fix(style): let background texture take precedence over overridden background color (#3761)
When a background texture is active, skip applying the overridden bg/fg/border
colors on block elements so the texture stays visible instead of being painted over.
2026-04-05 18:05:05 +02:00
Huang Xin 09d1e0c040 fix(sync): show last push time as the last sync time (#3760) 2026-04-05 17:55:41 +02:00
Huang Xin 8b10e7fb17 fix(layout): use mobile footer bar in portrait mode without regressing phone panel animation, closes #3742 (#3759)
On mobile tablets/foldables (Android or iOS) in portrait, the viewport
width can exceed the Tailwind \`sm:\` breakpoint (640px), causing the
desktop footer bar to show and hiding the brightness / font size /
color / progress panels that only exist in the mobile layout.

The previous fix in #3746 introduced a regression on phones: wrapping
MobileFooterBar's children in a \`<div>\` changed the flex layout of
the footer container, and panels no longer slid cleanly behind the
navigation bar on dismissal. That PR was reverted.

This re-implementation scopes the override narrowly:

- \`forceMobileLayout\` is only true for mobile devices in portrait
  with innerWidth >= 640. Phones (innerWidth < 640) always get
  \`false\`, so every \`!forceMobileLayout && '…'\` expression
  evaluates to the original class string — phone classNames are
  set-equal to the pre-#3746 version.
- MobileFooterBar keeps its Fragment return; no wrapper div is
  introduced anywhere in the tree, preserving the panel slide-down
  animation exactly as before.
- DesktopFooterBar, NavigationBar, and the three panels
  (ColorPanel / FontLayoutPanel / NavigationPanel) gate their
  \`sm:hidden\` / \`sm:flex\` classes on \`!forceMobileLayout\` so
  they correctly show/hide on wide portrait tablets.
- The orphaned \`.force-mobile-layout\` CSS override in globals.css
  is removed since we no longer rely on a class-based escape hatch.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 17:40:36 +02:00
Huang Xin a2d17e6a79 fix: clear highlight overlay when deleting annotation from sidebar, closes #3756 (#3758)
The sidebar delete handler always removed annotations through the note
bubble overlay key (`NOTE_PREFIX + cfi`), but highlights are keyed by
the raw CFI. Deleting a highlight from the sidebar therefore left its
overlay drawn until the book was reopened, while the popup path — which
lets `wrappedFoliateView` default the value to the CFI — worked.

Introduce `removeBookNoteOverlays` that mirrors the draw filters in
`Annotator.tsx` and clears every overlay a BookNote can own (highlight
and/or note bubble), and route the sidebar delete through it.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 16:25:58 +02:00
Huang Xin 7e62516b5d chore(deps): bump Next.js to version 16.2.2 (#3757) 2026-04-05 15:59:10 +02:00
Lex Moulton d53f3b42e2 feat(rsvp): split words option, faster countdown, and skip pages RSVP cant open (#3755)
* fix: use string-parsed year to avoid UTC/timezone boundary issues

* feat: Add option to split words in RSVP mode

* fix: change the checkbox to a toggle

* fix(rsvp): speed up countdown from 2.4s to 1.5s

* feat(rsvp): skip to next page when no readable text is found

* fix(rsvp): replace lookbehind regex with lookahead-only split in getHyphenParts
2026-04-05 14:29:30 +02:00
Mohammed Efaz d682fcbb44 feat: add named highlight colors with sync and picker ux fixes (#3741)
* fix: add highlight color label fields

* fix: add default highlight label sync fields

* fix: add highlight prefs sync helpers

* fix: add highlight color name inputs

* fix: persist and sync highlight color names

* fix: add highlight color label helpers

* fix: add long press highlight label preview

* fix: pull highlight color prefs during library sync

* test: cover highlight color label helpers

* fix: widen highlight color name inputs

* fix: show highlight names on hover and touch hold

* fix: prevent highlight name input overlap

* fix: improve highlight name input responsiveness

* fix: support drag scrolling for highlight colors

* fix: batch custom color and label updates

* fix: serialize highlight prefs saves

* fix: align color strip drag and restore color clicks

* fix: translate default highlight color labels

* refactor: remove highlight preference sync wiring

* fix: align highlight option i18n with existing pattern

* fix: remove redundant english highlight keys

* fix: support raw and normalized highlight label keys

* fix: use underscore translator in highlight options

* fix: translate custom highlight color labels in editor

* refactor: simplify highlight settings persistence

* fix: maintianer review

* refactor: simplify highlight prefs save and clean up editor

- Drop the skipUserColors/skipLabels options from handleHighlightPrefsChange
  and always persist both arrays; the flags only masked a no-op caller.
- Type handleHighlightColorsChange with Record<HighlightColor, string>
  instead of typeof so the signature reads clearly.
- Remove the always-true `|| true` guard around the custom colors section.
- Stop wrapping user-typed custom color labels with _(), matching the
  built-in color inputs and keeping the input value equal to what the
  user typed.

* refactor: couple highlight labels to their colors

Replace the parallel `highlightColorLabels: Record<string, string>` map
with label storage that lives next to each color. This removes the hex
key normalization layer and its whole class of orphan/case-drift bugs.

Data model:
  userHighlightColors: string[]                    ->  UserHighlightColor[]
                                                       ({ hex, label? })
  highlightColorLabels: Record<string, string>     ->  (removed)
                                                       defaultHighlightLabels:
                                                         Partial<Record<DefaultHighlightColor, string>>

A `migrateHighlightColorPrefs` helper runs during `loadSettings` and
handles both the shipped `string[]` layout and the draft-build
`highlightColorLabels` layout: hex-keyed labels attach to matching user
colors, name-keyed labels move into `defaultHighlightLabels`. Malformed
entries are dropped.

Editor:
  - Label inputs commit on blur (Enter also commits), so typing a long
    label no longer fires `setSettings`/`saveSettings` on every
    keystroke. A small `LabelInput` component owns the draft state and
    syncs if the prop changes externally.
  - Three explicit callbacks (`onCustomHighlightColorsChange`,
    `onUserHighlightColorsChange`, `onDefaultHighlightLabelsChange`)
    replace the previous single callback with opaque skip flags.

Picker:
  - Extracted a reusable `useDragScroll` hook (mouse only, 6px
    threshold, 120ms click suppression) from the inline state machine
    in `HighlightOptions`. The picker drops to ~280 lines.
  - Long-press preview stays touch/pen only. It now reads labels via
    `getHighlightColorLabel` (which returns `undefined` when no user
    label is set), letting the component layer decide whether to fall
    back to a translated default name.

i18n:
  - Default color names ('red' | 'yellow' | 'green' | 'blue' | 'violet')
    are registered once at module scope via `stubTranslation` and
    translated at the picker layer through `useTranslation`. User-typed
    labels are never run through `_()`, so what the user types is what
    the editor shows.

Tests:
  - `annotator-util.test.ts`: rewritten around the new helper contract
    (user label -> undefined fallback) and case-insensitive hex matching.
  - `settings-highlight-migration.test.ts`: new, covers legacy
    `string[]`, already-migrated entries, malformed hex filtering, and
    the two draft-label fold paths.

---------

Co-authored-by: Huang Xin <chrox.huang@gmail.com>
2026-04-05 18:08:55 +08:00
Huang Xin b3fe33221d feat(i18n): add Hungarian translation and translate new keys across all locales (#3753)
Add Hungarian (hu) as a new supported locale with full translation coverage.
Also translate the new "Toggle Toolbar" key across all 30 existing locales.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 00:15:48 +02:00
Huang Xin fbfd7fd6c6 fix(hardcover): use Tauri HTTP plugin to bypass CORS and coerce search result IDs to numbers, closes #3751 (#3752)
The Hardcover GraphQL API rejects requests from tauri://localhost origin on iOS.
Switch to tauriFetch (like KOSyncClient) to bypass CORS in Tauri environments.
Also coerce bookId/editionId/pages from search results to numbers since the
search API returns string IDs but the GraphQL mutations expect 32-bit integers.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 22:56:47 +02:00
Huang Xin 531dbe5f16 release: version 0.10.2 (#3750) 2026-04-04 21:23:25 +02:00
Huang Xin 70b94d8986 fix(layout): fixed layout of progress bar in vertical mode (#3749) 2026-04-04 21:20:34 +02:00
Huang Xin 21795e5cd3 fix(tts): avoid race condition in preloadNextSSML causing wrong highlights (#3748)
preloadNextSSML() called tts.next() interleaved with await, creating async
gaps where the concurrent #speak() could dispatch marks against corrupted
#ranges state (replaced by next() for a different block). Since mark names
restart at "0" per block, marks resolved to the wrong text position, causing
accidental page turns and highlights far from the current sentence.

Fix: gather all tts.next() and tts.prev() calls synchronously (no await
between them) so no async code can see the intermediate #ranges state.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 20:42:06 +02:00
Huang Xin 52242d6886 fix(android): use mobile footer bar in portrait mode on Android, closes #3742 (#3746)
On Android tablets/foldables in portrait, screen width can exceed 640px
causing the desktop footer bar to show. Now use portrait orientation
detection to force the mobile footer bar layout on Android regardless
of screen width.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 20:11:02 +02:00
Huang Xin f361698e05 feat(android): add D-pad navigation for Android TV remote controller (#3745)
* feat(android): add D-pad navigation for Android TV remote controller

Add basic D-pad/arrow key navigation support for Android TV Bluetooth
remote controllers, covering the full flow: library grid browsing,
reader page turning, toolbar interaction, and TTS toggle.

Library:
- Custom spatial navigation hook for grid D-pad navigation
- Arrow keys move between BookshelfItem elements with auto column detection
- ArrowDown from header enters the bookshelf grid

Reader:
- Enter key toggles header/footer toolbar visibility
- Left/Right navigate between toolbar buttons, Up/Down moves between
  header and footer bars
- Back button dismisses toolbar (with blur) before sidebar/library
- Focus-probe technique for reliable button visibility detection
  across fixed/hidden containers

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: only auto-focus toolbar buttons on keyboard activation, not mouse hover

Track pointer activity to distinguish keyboard vs mouse toolbar activation.
When the toolbar appears due to mouse hover, skip auto-focus to prevent
unwanted focus outlines on desktop.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: track keyboard events instead of pointer events for auto-focus guard

Pointer events from iframes don't bubble to the main document, so
tracking pointermove/pointerdown missed hover interactions over book
content. Track keydown instead — auto-focus only when a recent keyboard
event (Enter key) triggered the toolbar, not mouse hover.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 19:50:34 +02:00
Huang Xin 9595aa56e0 fix(android): get rid of the outline on the header and footer bar when using remote control to turn page (#3744) 2026-04-04 17:00:20 +02:00
Huang Xin caa0d719c5 compat(vertical): check writing mode also for child element of body, closes #3583 (#3743) 2026-04-04 15:56:26 +02:00
Yi-Shun Wang 45bd355981 feat(opds): support custom catalog headers with web proxy consent (#3740)
* Add custom headers support for OPDS catalogs

* Require web proxy consent for sensitive OPDS credentials

* Strengthen OPDS header forwarding tests

---------

Co-authored-by: Yi-Shun Wang <git@albertwang.dev>
2026-04-04 12:01:16 +02:00
Huang Xin 52ac74bbad fix: fixed status info layout in vertical mode, fixed Android build (#3735) 2026-04-03 21:41:49 +02:00
Huang Xin 6a44f609ba fix(paginator): fixed paginator section preloading, closes #3600 and closes #3601 (#3734) 2026-04-03 20:46:45 +02:00
Huang Xin ca5c860594 fix(kosync): don't normalize xpointer for more accurate progress sync, closes #3672 and closes #3616 (#3733) 2026-04-03 16:17:20 +02:00
AK Venugopal 80cab8e56d feat: Hardcover.app Sync (#3724)
* feat(hardcover): add one-way Hardcover sync integration

- Add HardcoverClient with ISBN/title-based book lookup, progress push, and note sync
- Add HardcoverSyncMapStore for persistent local mapping of note IDs to Hardcover journal IDs
- Add GraphQL queries/mutations for Hardcover API (insert/update/recreate journal entries)
- Add DB migration for hardcover_note_mappings table (schema: hardcover-sync)
- Add HardcoverSettings dialog component (connect/disconnect/enable toggle)
- Add useHardcoverSync hook wired in Annotator for push-notes and push-progress events
- Add Hardcover Sync section in BookMenu with per-book toggle (persisted in BookConfig)
- Add HardcoverSettings type and DEFAULT_HARDCOVER_SETTINGS to system settings
- Add hardcoverSyncEnabled per-book flag to BookConfig
- Mount HardcoverSettingsWindow in Reader alongside KOSync and Readwise windows

Sync is one-way (Readest → Hardcover), manual-action only, per-book opt-in.
Supports idempotent note sync: insert new, skip unchanged, update changed, recreate on stale remote ID.

* fix(hardcover): proxy API calls server-side to fix CORS, normalize Bearer token

- Add /api/hardcover/graphql Next.js route that proxies POST requests server-side,
  bypassing CORS restrictions (Hardcover API has no Access-Control-Allow-Origin header)
- On Tauri (desktop), calls Hardcover directly; on web, routes through the proxy
- Normalize token in HardcoverClient: accept raw JWT or 'Bearer <jwt>' format
- Update helper text to point to hardcover.app → Settings → API

* fix(hardcover): surface note-sync no-ops and harden book resolution

- Show toast feedback when book data is still loading, Hardcover is not configured,
  or the current book has no annotations/excerpts to sync
- Show an explicit info toast when note sync finds no new changes
- Parse Hardcover search results in the current hits/document response shape
- Resolve note sync through ensureBookInLibrary for parity with progress sync
- Add console logging for note/progress sync failures

* debug(hardcover): add runtime instrumentation for note sync

* feat(metadata): keep identifier stable and store ISBN separately

- Add dedicated metadata.isbn field
- Expose ISBN as its own editable field in book metadata
- Preserve identifier semantics for existing source IDs and hashes
- Route metadata auto-retrieval ISBN handling through the new field
- Prefer metadata.isbn for Hardcover matching

* fix(hardcover): avoid wasm sqlite for note mappings on web

- Store Hardcover note sync mappings in localStorage on web
- Keep sqlite-backed mappings for desktop/native environments
- Remove the web-only database dependency from manual note sync

* fix(hardcover): dedupe notes by payload hash across unstable note IDs

- Add payload-hash lookup in HardcoverSyncMapStore
- Reuse existing journal mapping when payload already synced
- Prevent duplicate insertions when note IDs change or duplicate locally

* fix(hardcover): avoid duplicate quote export when annotation note exists

- Detect excerpt+annotation pairs for the same highlight
- Skip standalone excerpt export when annotation has note text
- Keep annotation export as the single source of truth

* docs(hardcover): add consolidated change summary for review

* fix(hardcover): suppress excerpt export by CFI when annotation note exists

* fix(hardcover): suppress empty-note annotation duplicates when note exists

* fix(hardcover): deduplicate notes by text and cfi base node

* refactor(hardcover): optimize sync performance, add rate limiting and clean up debug tools

* chore: remove dev-only change log from main

* test(hardcover): add unit tests for sync mapping and client logic

* chore: custom deployment and UI fixes

* fix(hardcover): use timestamptz for accurate annotation time

* fix(hardcover): use date scalar and RFC 3339 formatting for journal entries

* Revert "chore: custom deployment and UI fixes"

This reverts commit 0329aba7129d1e1ebf2c663804b8fba9a9f87b91.

* Fix hardcover progress dates and surface imported ISBNs

* Fix Hardcover currently reading sync

* fix(hardcover): avoid promoting note sync status

* style(hardcover): apply prettier formatting

* test(hardcover): fix strict TypeScript assertions

* test(hardcover): harden sync regression coverage

* test(hardcover): fix lint and formatting regressions

* fix(hardcover): narrow note dedupe range matching

* refactor(hardcover): extract note dedupe helpers

* refactor(isbn): extract metadata normalization helpers

* feat(hardcover): improve synced quote formatting
2026-04-03 09:06:43 +02:00
Mohammed Efaz 888f4afde9 fix: preserve paragraph mode reading layouts and other UI/UX fixes (#3730)
* fix: paragraph mode

* test: paragraph mode

* test: remove paragraph mode

* fix: paragraph utils

* fix: paragraph hook

* fix: paragraph overlay

* fix: paragraph bar

* fix: paragraph control

* fix: paragraph shortcuts

* test: paragraph utils

* test: paragraph hook

* test: paragraph overlay

* test: paragraph shortcuts

* fix: paragraph overlay

* test: paragraph mode

* test: shortcuts

* test: remove overlay

* test: remove hook

* test: remove utils

* fix: paragraph overlay

* fix: paragraph overlay

* feat: paragraph overlay

* fix: vertical container sizing

* test: paragraph container

* fix: paragraph animation

* fix: paragraph text animation

* fix: remove container morph
2026-04-02 20:56:38 +02:00
Huang Xin 05afaab5fd fix(layout): fixed static image size and layout shift on window resize, closes #3634 (#3729) 2026-04-02 20:50:02 +02:00
Huang Xin ff962a1f02 fix(android): auto-shutdown native TTS engine after 30 min idle to save battery (#3728)
When the Android native TTS engine is paused or stopped but not shut down,
it holds resources and drains battery. This adds a 30-minute idle timer that
automatically shuts down the TextToSpeech engine and MediaPlaybackService
after inactivity. The engine transparently re-initializes on next use.

Also adds missing androidx.lifecycle:lifecycle-process dependency to fix
ProcessLifecycleOwner build error.

Closes #3713

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 20:00:21 +02:00
Huang Xin 62df631dd1 feat(theme): add atmosphere easter egg with video overlay and ambient audio (#3727)
Hidden easter egg: re-clicking the active light mode sun icon
or dark mode moon icon activates an ambient atmosphere with a
komorebi leaf shadow video overlay and forest background audio. Audio
adapts to theme: birds for light mode, crickets for dark mode.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 16:12:10 +02:00
Lex Moulton c9647276b1 feat(rsvp): progress bar per chapter, speed selector dropdown, and UX improvements (#3723)
* fix(rsvp): rename pause setting to punctuation delay for clarity

* fix(rsvp): update chapter header as user crosses TOC sections

* fix(rsvp): stop playback at end of book instead of restarting last chapter

* feat(rsvp): convert WPM badge to speed selector dropdown

* feat(rsvp): scroll active chapter to center when chapter dropdown opens

* fix(rsvp): show correct chapter name in header instead of "Select Chapter"

* fix(rsvp): start from selected chapter when switching via dropdown

* fix(rsvp): restore saved position when resuming from a different section

* fix(rsvp): remove shrink animation from header buttons on click

* refactor(rsvp): process one spine section at a time, greadtly reducing complexity

* refactor(rsvp): remove dead state and derive progress from currentIndex

* feat(rsvp): allow pausing during countdown

* fix(rsvp): use relocate event instead of fixed 500ms delay for chapter transitions

* fix(rsvp): persist WPM and punctuation pause settings across sessions

* fix(rsvp): prefix unused bookKey field with underscore to satisfy lint

* Revert "fix(rsvp): prefix unused bookKey field with underscore to satisfy lint"

This reverts commit 7b3a34813f0c3771e9af8d00c7c7dc77c8db161c.

* fix(rsvp): remove unused bookKey field from RSVPController

* feat(rsvp): make progress bar draggable

* fix: revert settings.json changes

* fix(rsvp): restore genuinely useful comments

* i18n: update translations

---------

Co-authored-by: Huang Xin <chrox.huang@gmail.com>
2026-04-02 03:31:58 +02:00
Mohammed Efaz 9ecb9b24d2 feat: make reading ruler selection and step navigation coherent (#3722)
* refactor(reader): extract reading ruler math helpers

* fix(reader): keep text selectable inside reading ruler

* feat(reader): route taps to ruler step navigation

* feat(reader): route keyboard navigation to ruler steps

* fix(reader): animate ruler mask and frame together

* fix(reader): preserve drag anchor for ruler handles

* fix(reader): fall back to page turns at ruler edges
2026-04-01 18:59:44 +02:00
Huang Xin f0ab05bbde chore(agent): update agent skills and memories (#3721) 2026-04-01 16:44:52 +02:00
Huang Xin f1a08565e3 fix(storage): paginate stats query and align file size formatting (#3720)
Paginate Supabase select queries in the storage stats API to avoid
the 1000 row default limit. Align StorageManager's formatFileSize
with useQuotaStats calculation so quota values display consistently.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 16:44:15 +02:00
Huang Xin b8ddb5475e feat(sync): add full sync option for annotations in koplugin, closes #3710 (#3718)
Add "Full sync all annotations" menu item that pushes and pulls all
annotations regardless of the last sync timestamp, enabling users to
sync old highlights that were created before the plugin was installed.

Changes:
- Add full_sync parameter to push/pull that bypasses timestamp filter
  and uses since=0 for pulling all server annotations
- Deduplicate by annotation ID alongside position-based dedup
- Store server ID on pulled annotations and reuse it when pushing
- Parse ISO 8601 timestamps from server to preserve original
  created/updated dates instead of using current time
- Resolve KOReader page numbers from xpointers via getPageFromXPointer
- Resolve Readest page numbers from CFI via getCFIProgress on pull

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 15:35:48 +02:00
Huang Xin 74401fc1bb fix(library): always sort series books by index ascending, closes #3709 (#3717)
* fix(library): always sort series books by index ascending, closes #3709

When grouping by series, the sort direction (asc/desc) was applied to
within-series book sorting, causing books to appear in reverse series
index order when the library was sorted descending. Now series index
sort is always ascending (1, 2, 3…) regardless of the global sort
direction. Also sort series groups by name when "Sort by Series" is
selected instead of falling back to updatedAt.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(library): skip merge sort when inside a series group

The allItems.sort at the end of sortedBookshelfItems was re-sorting
books using the regular bookSorter (e.g. Date Read), which overrode
the within-group sort that correctly prioritized series index. Now
when inside a group, the already-sorted books are returned directly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 14:01:48 +02:00
Huang Xin 29df8522fa chore(bump): bump Tauri to the latest version (#3716) 2026-04-01 10:06:23 +02:00
Teodor-Mihai Cosma 9f958a44e2 feat(i18n): add Romanian (ro) translation (#3708)
* feat(i18n): add Romanian (ro) translation

* chore: update tauri submodule for Romanian language support
2026-04-01 09:22:04 +02:00
Huang Xin 0e516f6e56 chore(test): add unit tests and enforce dash-case naming for test files (#3715)
Add ~290 new unit tests covering utils (lru, diff, css, a11y, walk,
usage, txt-worker), services (EdgeTTSClient, transformers), and
suppress noisy console output across all test files. Rename 27
camelCase test files to dash-case for consistency.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 09:14:00 +02:00
Huang Xin b71b246601 feat(settings): add TTS settings tab and highlight opacity, closes #3661 (#3712)
Add a new TTS tab in settings with media metadata update frequency control
(sentence/paragraph/chapter) to reduce Bluetooth notification spam, and move
TTS highlight settings from Color tab to the new TTS tab. Also add a highlight
opacity setting with live preview in the Color tab.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 06:35:26 +02:00
Huang Xin 94843902ac fix(annotations): fix all annotations grouped under last chapter for fragment-href TOCs, closes #3688 (#3706)
When TOC entries use fragment-suffixed hrefs (e.g. ch01.xhtml#ch01),
the sectionsMap lookup matched subitems with cfi: undefined instead of
parent sections with valid CFIs. This caused findTocItemBS to map every
annotation to the last TOC entry. Now skip subitems lacking a CFI and
fall back to the base section.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 17:56:23 +02:00
Huang Xin f67930feb1 fix(opds): fix Copyparty books showing as "Untitled" in mixed feeds, closes #3667 (#3705) 2026-03-31 17:35:14 +02:00
Huang Xin 5e048ddab1 fix(iOS): use correct system theme mode in auto mode on iOS, closes #3698 (#3704) 2026-03-31 17:06:08 +02:00
Huang Xin e68dedd10d fix(layout): fix primary view detection on fractional DPR devices, closes #3681 (#3701) 2026-03-31 15:04:02 +02:00
Huang Xin d22b8bec1e i18n: update translations for aria label (#3697) 2026-03-30 19:10:31 +02:00
Huang Xin e9c5ebb696 fix(fonts): fix Adobe font deobfuscation and CSS var fallbacks, closes #3662 (#3696)
Two issues caused embedded EPUB fonts to fail:

1. Adobe font deobfuscation (http://ns.adobe.com/pdf/enc#RC) derived the
   XOR key from the wrong UUID. getUUID() picked the first UUID across all
   dc:identifier elements (e.g. Calibre's internal UUID) instead of the
   book's unique-identifier. Also fixed getElementById not working on
   DOMParser-parsed XML (no DTD), and made UUID regex case-insensitive.

2. transformStylesheet replaced generic font families (sans-serif, serif,
   monospace) with CSS var() references without fallback values. In
   fixed-layout iframes where setStyles doesn't inject the variables,
   the undefined var() invalidated the entire font-family declaration
   per CSS spec, causing all fonts to fall back to system defaults.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 19:06:22 +02:00
Huang Xin 956c71cd7b chore: migrate from ESLint to Biome for linting (#3694)
Replace ESLint with Biome for ~14x faster linting (~360ms vs ~5s).

- Add biome.json with rules matching ESLint parity (Next.js, a11y,
  TypeScript, unused vars/imports)
- Remove eslint, @typescript-eslint/*, eslint-config-next,
  eslint-plugin-jsx-a11y, @eslint/* from deps
- Remove eslint.config.mjs
- Update lint script to: tsgo --noEmit && biome check .
- Fix 11 real code issues caught by Biome (banned types, explicit any,
  unsafe finally, unreachable code, shadowed names)
- Disable Biome formatter (Prettier stays for Tailwind class sorting)

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 16:25:04 +02:00
Huang Xin b4207bd742 chore(deps): bump vulnerable dependencies to address Dependabot alerts (#3693) 2026-03-30 15:59:03 +02:00
Huang Xin ec26ef4f29 fix(shortcuts): change bookmark shortcut from Ctrl+D to Ctrl+B (#3691)
* fix(shortcuts): change bookmark shortcut from Ctrl+D to Ctrl+B, closes #3675

Ctrl+D was bound to both Toggle Bookmark (General) and Dictionary
Lookup (Selection). When text was selected and Ctrl+D pressed, both
actions fired — opening the dictionary AND adding an unwanted bookmark.

Changed bookmark to Ctrl+B/Cmd+B to resolve the conflict. Added a
unit test that detects identical keybinding lists across actions to
prevent this class of bug in the future.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* i18n: mark shortcut section names for translation

Wrap SHORTCUT_SECTIONS values with stubTranslation() so i18next-scanner
picks them up. Translate the 4 new keys (General, Text to Speech, Zoom,
Window) across all 29 locales.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 15:35:40 +02:00
Huang Xin b872868136 fix(layout): fixed infinite expand calls and freeze in the paginator, closes #3683 (#3692) 2026-03-30 15:16:56 +02:00
Huang Xin 797fe9c604 fix(layout): fixed infinite expand calls and freeze in the paginator, closes #3683 (#3690) 2026-03-30 14:50:59 +02:00
Huang Xin 8ed9290659 layout: don't truncate remaining progress info without status info, closes #3678 (#3685) 2026-03-30 05:52:09 +02:00
Huang Xin 8e6451863f css: add css selector for status badge, closes #3678 (#3684) 2026-03-30 05:19:29 +02:00
Huang Xin c688612888 chore(fdroid): build qcms wasm for fdroid (#3680) 2026-03-29 20:08:08 +02:00
Huang Xin b3333c384c chore(fdroid): get rid of wasm binaries in fdroid build (#3677) 2026-03-29 18:31:46 +02:00
Huang Xin 84349ab12d chore(release): exclude turso wasm in app builds (#3674) 2026-03-29 06:35:39 +02:00
Huang Xin c4e3315642 feat(scroll): add single section scroll option, closes #3663 (#3668) 2026-03-28 13:57:20 +08:00
Huang Xin bbfc82e50d feat(android): add foss flavor build without gms services (#3666) 2026-03-28 05:36:09 +01:00
Huang Xin 966f5e2acd fix(opds): fixed image download from ODPS server on the web, closes #3649 (#3658) 2026-03-27 13:21:52 +01:00
Huang Xin 45e5f0fa61 fix(eink): disable range editor loupe for annotations on Eink devices, closes #3655 (#3656) 2026-03-27 10:26:36 +01:00
Huang Xin 3d4d1482aa feat: add keyboard shortcuts help dialog (#3653)
Add a keyboard shortcuts help dialog toggled with '?' that displays all
shortcuts grouped by section with platform-appropriate key rendering.

Restructure DEFAULT_SHORTCUTS to include i18n descriptions and section
metadata. Translate 37 new keys across 29 locales.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 14:59:38 +08:00
Huang Xin 5f897f648f feat(tts): add shortcuts to navigate and play/pause in TTS mode, closes #3620 (#3651) 2026-03-27 06:39:17 +01:00
Huang Xin 26fec924fc chore: bump turso to the latest version (#3650) 2026-03-27 06:28:59 +01:00
Huang Xin af9cf33936 fix(android): never try to fight with the navigation bar on Android ever, closes #3618 (#3646) 2026-03-26 17:40:49 +01:00
Huang Xin 52df478f21 fix: show proper background images in continuous scrolled mode, closes #3638 (#3645) 2026-03-26 16:41:55 +01:00
Huang Xin 97555a7e88 chore: bump next.js, opennextjs and wrangler to the latest versions (#3642) 2026-03-26 15:57:19 +01:00
Lee Kai Ze c7e82825f5 fix(koplugin): avoid resurrecting deleted annotations on pull (#3639)
Closes #3621.
2026-03-26 15:13:32 +01:00
dependabot[bot] bb82ab6c8a chore(deps): bump android-actions/setup-android (#3631) 2026-03-26 14:01:27 +08:00
Huang Xin e2faa9ad75 compat(android): disable native long-click on the WebView to prevent the system image context menu, closes #3629 (#3630) 2026-03-26 04:12:54 +01:00
Huang Xin f310305834 fix(library): mixed sorting for group and ungroupped books, closes #3596 (#3627) 2026-03-25 16:55:20 +01:00
Huang Xin 5a072e7d1f fix(pdf): apply theme colors for PDFs, closes #3593 (#3626) 2026-03-25 16:50:12 +01:00
Huang Xin 64675820f1 fix(koplugin): fixed koreader crash on logout, closes #3598 (#3603) 2026-03-23 15:08:51 +01:00
Huang Xin 1e942a23b4 chore(release): generate changelog from release notes for google play (#3591) 2026-03-22 16:38:26 +01:00
Huang Xin a92c357982 chore(release): disable linux-arm build for now as turso can't work on it for now (#3590) 2026-03-22 14:53:45 +01:00
Huang Xin 1ebf5e7b52 fix(release): skip architecture check for 32-bit ARM (#3589) 2026-03-22 14:22:22 +01:00
Huang Xin c11f79e843 release: version 0.10.1 (#3588) 2026-03-22 13:29:13 +01:00
Huang Xin 87f0240b0a compat(footnote): support footnote text in alt attribute of the image, closes #3576 (#3587)
refactor: remove unused continuous scroll
2026-03-22 12:17:02 +01:00
Huang Xin 2905506017 fix(layout): fixed total scrollable width in vertical scrolled mode, closes #3583 (#3586) 2026-03-22 11:56:12 +01:00
Huang Xin 1936136596 fix: resolve various tracked exceptions in ph (#3584)
* fix: handle synced bookmarks without cfi

* chore: clean up some exceptions in ph
2026-03-22 08:16:38 +01:00
Huang Xin e0cf7e8d9f fix: handle number input value clip in settings on mobile devices (#3582) 2026-03-22 05:26:21 +01:00
Huang Xin 0177a03a90 fix(tts): properly follow tts reading position in scrolled mode (#3581) 2026-03-21 18:08:45 +01:00
Huang Xin f5df80f154 feat(koplugin): support sync bookmarks between Koreader and Readest devices (#3580) 2026-03-21 17:58:38 +01:00
Huang Xin 76b239f382 feat(koplugin): add support for annotation sync (#3579)
Add bidirectional annotation/highlight sync between KOReader and Readest:

- Add xpointer0/xpointer1 fields to BookNote and DBBookNote types for
  KOReader XPointer positions alongside Readest's CFI format
- Extend transform layer to pass through xpointer fields to/from DB
- Convert CFI→XPointer on push and XPointer→CFI on pull in useNotesSync,
  discarding notes that fail conversion
- Support KOReader's text()[K].N indexed text node format in xcfi.ts for
  paragraphs with inline elements (e.g. <a> page anchors)
- Generate KOReader-compatible XPointers: text().N for single text nodes,
  text()[K].N only when multiple direct text nodes exist
- Skip cfi-inert elements (injected by Readest at runtime) in XPointer
  path building and resolution
- Map highlight colors between KOReader and Readest color systems
- Implement KOReader plugin annotation push/pull with deterministic IDs,
  auto-sync on document open/close, and UIManager refresh on pull
- Refactor koplugin into focused modules: syncauth, syncconfig,
  syncannotations, selfupdate

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 16:43:51 +01:00
Huang Xin 2f430bf2ff feat(koplugin): add self-update check and install from menu (#3575)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 08:16:50 +01:00
Huang Xin 0e8605d298 feat(reader): import Foliate annotations on book open (Linux only), closes #2180 (#3574)
* feat(reader): import Foliate annotations on book open (Linux only), closes #2180

Automatically imports annotations, bookmarks, and reading progress from
Foliate's data files when opening a book on Linux. Uses foliateImportedAt
flag to prevent re-importing on subsequent opens.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor(annotation): move Foliate import into annotation provider pattern

Restructure annotation import as a multi-provider service following the
translators pattern. Foliate becomes a provider under
services/annotation/providers/, making it easy to add more import sources.
Each provider controls its own availability check and skip-if-imported guard.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 05:04:46 +01:00
Huang Xin 7445d5befe feat(library): add batch refresh metadata option, closes #3294 (#3573)
Add a "Refresh Metadata" option under Advanced Settings that re-parses
book files to update metadata (series info, language, etc.) for books
imported before these fields were supported.

- Add refreshBookMetadata() in bookService that opens a book file,
  re-parses with DocumentLoader, and updates metadata/metaHash/series
  without touching updatedAt or user-edited fields
- Add menu item in SettingsMenu with progress display
- Add unit tests for single-book metadata refresh
- Translate new i18n strings across all 29 locales

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 19:32:09 +01:00
Huang Xin e41bcfbac9 fix(txt): fix chapter detection for bare numbered headings, closes #3570 (#3572)
Three bugs in createChapterRegexps for English txt-to-epub conversion:

1. numberPattern had a capturing group (\d+|...) that leaked single
   digits into split() results instead of chapter titles. Changed to
   non-capturing (?:\d+|...).

2. combinedPattern wrapped the heading in (?:...) so split() consumed
   the title without returning it. Changed to (...) capturing group,
   matching the Chinese regex structure.

3. The prefix (?:^|\n|\s) allowed matching Chapter/Section after any
   whitespace, causing mid-sentence false positives. Changed to
   (?:^|\n) for line-start only.

Added a second-tier regex for bare numbered headings (e.g. "1.1The
Elements of Programming", "1Building Abstractions") commonly found
in academic texts. Dotted numbers allow optional space before title;
single bare digits require immediate title to avoid footnote matches.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 19:00:33 +01:00
Huang Xin 91bc4ddec7 feat(library): backup to and restore from a zip file (#3571) 2026-03-20 18:27:52 +01:00
Huang Xin e8f70b896e chore: bump next.js from 16.1.6 to 16.1.7 (#3569) 2026-03-19 18:11:26 +01:00
Huang Xin 1f4f5f8a6d feat(annotator): responsive layout for the loupe (#3568) 2026-03-19 17:54:24 +01:00
Huang Xin 764acf59de chore(agent): add project memory (#3567) 2026-03-19 17:46:45 +01:00
Huang Xin eab83c6d3f feat(annotator): show the loupe when selecting text in instant annotation, closes #3539 (#3565) 2026-03-19 15:10:33 +01:00
dependabot[bot] 7f62cdcab9 chore(deps): bump pnpm/action-setup in the github-actions group (#3564) 2026-03-19 13:06:00 +08:00
Huang Xin e4ab06abcd fix(footnote): don't preload other sections in footnotes (#3563) 2026-03-19 04:10:16 +01:00
Huang Xin fef0aa6947 fix(perf): revert back next/image with lazying loading for much better perf (#3562) 2026-03-18 14:49:06 +01:00
Huang Xin 2935eacb1c fix(layout): fixed header background color on mobile devices (#3561) 2026-03-18 13:08:43 +01:00
Huang Xin ad9bb58cd4 fix(ios): resolve stale closure crash preventing highlight popup on iOS (#3559) 2026-03-18 10:11:13 +01:00
Huang Xin 871d6467c3 fix(layout): more responsive layout for the transfer queue (#3558) 2026-03-18 06:32:55 +01:00
Huang Xin 3f19ce24de chore(agent): add gstack skills (#3557) 2026-03-18 06:08:48 +01:00
Huang Xin 8280585e70 compat(css): fix table layout regression issue, closes #3551 (#3556) 2026-03-17 17:52:07 +01:00
Huang Xin 1f7483b911 feat: scroll to the nearest item in the search results or annotation lists (#3555) 2026-03-17 16:01:56 +01:00
Huang Xin b2bb92036b fix(ios): decode percent-encoded file URIs in copy_uri_to_path (#3553) 2026-03-17 13:17:42 +01:00
Huang Xin 3831f24ff7 fix(android): fix page navigation layer z-index, closes #3511 (#3552) 2026-03-17 09:52:21 +01:00
Huang Xin 3bec89c85d fix(progress): show remaining pages for current section instead of all loaded sections (#3550) 2026-03-17 09:18:39 +01:00
Huang Xin 3dfe6bd65e fix(layout): fix parallel read layout on smaller screens (#3549) 2026-03-17 07:34:55 +01:00
Huang Xin b00d321817 refactor(reader): extract shared panel drag hooks and add vertical drag to Notebook (#3548)
Extract useSwipeToDismiss and usePanelResize hooks from duplicated code
in SideBar and Notebook. Add mobile swipe-to-dismiss drag handle to
Notebook matching SideBar's existing behavior. Fix drag cursor showing
col-resize instead of row-resize during vertical drags.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 04:27:27 +01:00
Huang Xin 1af55e24da feat(epub): support continuous scroll and spread layout for EPUBs, closes #3419 and closes #1745 (#3546) 2026-03-16 18:24:07 +01:00
Huang Xin f7b2a2432c fix(export): apply block quote syntax to every line in annotation export, closes #3534 (#3536)
Add formatBlockQuote utility and blockquote nunjucks filter so both
default and custom template exports prefix every line with `>`.
Document all available formatters in the custom template help panel.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 15:23:17 +01:00
Huang Xin db3dee025b feat(booshelf): dedupe books with the same meta hash (#3535) 2026-03-14 15:14:22 +01:00
Huang Xin 25352efece chore: bump dependencies for Dependabot alerts (#3533) 2026-03-14 06:57:43 +01:00
Huang Xin 1297e33c62 doc(agent): add rules files that are automatically loaded into context alongside CLAUDE.md (#3532) 2026-03-14 04:37:41 +01:00
Huang Xin 8274c6bc4f feat(node): refactor appService into focused service modules and add app service for Node runtime (#3530)
This also closes #3431.
2026-03-13 17:51:35 +01:00
Huang Xin ed5bbd25d6 fix(opds): more accurate error message when downloading books, closes #2656 (#3529) 2026-03-13 08:26:54 +01:00
Huang Xin d06e1849cc feat(rsvp): add persistent context, display settings, and layout improvements, closes #3333 (#3528)
* feat(rsvp): add persistent context, display settings, and layout improvements, closes #3333

Add always-visible collapsible context panel, font size adjustment,
ORP color selection, and stable context window that only rebuilds on
scroll. Refactor speed controls into playback row with collapsible
settings behind a gear icon. Translate new i18n keys across 29 locales.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(selfhost): update docker db schema to match FileRecords (#3527)

In #2636, FileRecord was defined to have updated_at  field
which is used when it is accessed from the database. But the
local dev setup is missing this field.

This diff adds an updated_at column to match the expectation

* chore(ci): cache system dependency and rustc outputs in ci

* chore(ci): lint script changed from tsc to tsgo

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Azeem Bande-Ali <me@azeemba.com>
2026-03-13 06:57:47 +01:00
Azeem Bande-Ali 34ad6e6749 fix(selfhost): update docker db schema to match FileRecords (#3527)
In #2636, FileRecord was defined to have updated_at  field
which is used when it is accessed from the database. But the
local dev setup is missing this field.

This diff adds an updated_at column to match the expectation
2026-03-13 05:15:55 +01:00
Huang Xin b163012488 fix(link): prevent opening duplicate browser tabs on Tauri, closes #3514 (#3525) 2026-03-12 17:29:04 +01:00
Huang Xin f9a3559086 fix(library): improve sorting and grouping interaction, closes #3517 (#3524)
- Sort author groups by last name instead of first name when sorting by author
- Sort series groups by book author instead of series name when sorting by author
- Place ungrouped books before custom groups instead of mixing them together

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 16:59:50 +01:00
Huang Xin 674a0bf16d compat(footnote): support more footnote selectors, closes #3515 (#3523) 2026-03-12 16:46:57 +01:00
Huang Xin 9b53ccc9de fix(i18n): handle POSIX locale values in getLocale(), closes #3518 (#3522)
When LANG=C.UTF-8, navigator.language can return 'C' which is not a
valid BCP 47 tag, causing Intl.NumberFormat and toLocaleString to throw.
Normalize POSIX values (C, C.UTF-8, POSIX) to 'en-US' at the source.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 16:36:48 +01:00
Huang Xin 7a605a357a feat(pdf): support pitch to zoom and scroll mode for PDFs, closes #3461 (#3520) 2026-03-12 16:07:55 +01:00
Huang Xin af649edd0d fix(pdf): show search results when navigating to new page, closes #3148 (#3513) 2026-03-11 17:55:58 +01:00
Huang Xin 64a71e25e4 fix(layout): make horizontal scroll bar visible in fixed-layout EPUB, closes #3506 (#3512)
And fix some text not shown in fixed-layout EPUB.
2026-03-11 15:03:05 +01:00
Huang Xin 51a3767940 feat(a11y): show prev/next section buttons for screen reader, closes #3290 (#3511) 2026-03-11 13:33:36 +01:00
Huang Xin a8572ab7c6 feat(reader): tap notch area to scroll to top in scrolled mode, closes #3474 (#3510) 2026-03-11 18:20:38 +08:00
Huang Xin 907b672313 fix(a11y): improve TOC screen reader accessibility for NVDA, closes #3477 (#3509) 2026-03-11 14:30:01 +08:00
Huang Xin 8df9f909b4 fix(reader): add Always on Top toggle to reader view, closes #3482 (#3505)
- Apply alwaysOnTop setting on reader window init so books opened
  in a new window correctly inherit the setting
- Update tauriHandleSetAlwaysOnTop to apply to all open windows
  so toggling from any window keeps all windows in sync

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 13:15:08 +01:00
Huang Xin bcd7a90f27 fix(layout): add top/bottom margin to container in scrolled mode, closes #3463 (#3504)
* fix(layout): add top/bottom margin to container in scrolled mode, closes #3463

When showMarginsOnScroll is enabled, the paginator margins are set to 0
but the FoliateViewer container had no compensating padding, causing
header/footer bars to overlap content. Now the container padding aligns
content top to the bottom of the header bar (gridInsets.top + 44px) and
content bottom to the top of the footer bar (52px + safe area padding).
Also fixes the footer bar height constant from 44 to 52 to match
ProgressBar's actual h-[52px].

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore(ci): cache all crates including local path deps in Tauri build

Add cache-all-crates: 'true' to Swatinem/rust-cache so that vendored
local path dependencies (packages/tauri/crates/, packages/tauri-plugins/)
are cached between CI runs instead of being recompiled from scratch.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 10:40:03 +01:00
Huang Xin 5646e0cfb5 fix(txt): add stream() to RemoteFile to fix large TXT import on desktop, closes #3495 (#3502)
RemoteFile (used on all desktop platforms) extended File([]) with empty
data and overrode slice(), text(), arrayBuffer() but not stream(). The
large file path (>8MB) introduced in #3320 uses file.stream() to read
content incrementally, which returned empty data from the base File
class, causing "No chapters detected" for large TXT files.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 19:00:44 +01:00
Huang Xin fad27d74a0 compat(android): show button navigation bar on Android, closes #3466 (#3501) 2026-03-09 17:36:13 +01:00
Huang Xin 5fbbfa523b chore(ci): fix rust cache (#3500) 2026-03-09 15:32:01 +01:00
Huang Xin 569c6707a4 fix(macOS): fix traffic light visibility in sidebar, closes #3488 (#3499) 2026-03-09 15:10:13 +01:00
Huang Xin 99f8a29326 fix(css): apply Line Spacing to list elements, closes #3494 (#3498) 2026-03-09 14:01:44 +01:00
Huang Xin 04737d6f35 fix(layout): update safe insets in reader page, closes #3469 (#3497) 2026-03-09 13:54:03 +01:00
Huang Xin 8850e6c00f feat(pdf): support TTS and annotation on PDFs, closes #2149 & #3462 (#3493)
* chore: bump jsdom to the latest version

* feat(pdf): support TTS and annotation on PDFs, closes #2149 & closes #3462
2026-03-09 10:28:19 +01:00
Huang Xin 93b96d64eb fix(sidebar): use position fixed and transform for mobile sidebar (#3490)
* chore: bump nodejs version to 24

* fix(sidebar): use position fixed and transform for mobile sidebar

Use position: fixed to prevent horizontal scrolling on the mobile
bottom sheet, and replace style.top with transform: translateY() for
smooth drag performance. Cache element refs to avoid
document.querySelector on every drag frame. Apply the same position:
fixed fix to the notebook panel. Closes #3492

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 18:04:37 +01:00
srsng 9f8894c1e0 feat(setting): add an option to hide Scrollbar in scroll mode, closes #3480
* feat(setting) impl settings option: hide Scrollbar of scroll mode

* refactor(setting): disable hide scrollbar in paginated mode, add i18n translations

- Add disabled prop to Hide Scrollbar toggle when not in scroll mode
- Remove unnecessary `as const` assertion on string literal
- Add "Hide Scrollbar" translations for all 28 locales

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Huang Xin <chrox.huang@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 05:21:58 +01:00
Huang Xin dac4f2e8ec chore: experimental vinext build (#3486) 2026-03-06 18:04:28 +01:00
Huang Xin 54bc1514df feat(database): add platform-agnostic schema migration system (#3485) 2026-03-06 20:07:26 +08:00
Huang Xin 97cab2d70b feat(database): support turso fts in tauri apps (#3484) 2026-03-05 21:00:15 +01:00
Huang Xin 13588b4a65 chore(testing): add Tauri integration tests and E2E test infrastructure (#3483)
Set up WebDriver-based testing for the Tauri app with two tiers:
- Vitest browser-mode tests (*.tauri.test.ts) running inside the Tauri WebView
  for plugin IPC testing (libsql, smoke tests)
- WDIO E2E tests (*.e2e.ts) for UI-level interaction testing

Key changes:
- Add webdriver Cargo feature gating tauri-plugin-webdriver
- Add runtime capability for remote URLs (webdriver builds only)
- Add vitest.tauri.config.mts and wdio.conf.ts connecting to embedded
  WebDriver server on port 4445
- Add shared tauri-invoke helper for IPC from Vitest iframe context
- Add testing documentation in docs/testing.md
2026-03-05 19:14:01 +01:00
Huang Xin 0f5bd5ca1c fix(footnote): add overflow-wrap to footnote popup to prevent long words from overflowing (#3479)
The change adds overflow-wrap: break-word to the footnote popup body styles, which ensures long unbreakable strings (like URLs or long words) wrap properly instead of overflowing
the popup container.
2026-03-05 10:49:28 +01:00
Huang Xin a74c1a56a9 fix(metadata): parse series info from epub only when book series info not available (#3478) 2026-03-05 09:53:28 +01:00
Huang Xin 1e9bd1d821 chore(database): unit testing and feature detect for fts and vector search (#3476) 2026-03-05 07:41:16 +01:00
Huang Xin 02cc0a9ebb chore: bump turso to 0.5.0 (#3475) 2026-03-04 19:54:58 +01:00
Huang Xin 5273ef75dc feat(database): add database service abstraction with libsql/turso backend (#3472) 2026-03-05 02:38:23 +08:00
Blyrium bd957a4eb8 i18n: added <0> and <1> tags for SL (#3460) 2026-03-04 03:06:00 +01:00
scinac 38552a0c2e feat(reader): adding current Time and Battery to Footer (#3306) (#3402)
* added current time to desktop bar

* added time prototype to footer, needs code cleanup and settings toggle

* fixed settings toggle, added translations and code cleanup

* added battery support and moved Statusbar to own Component

* #3306 added 24 hour clock support

* refactored code styling and getting rid of any type in battery hook

* Add battery info for Tauri Apps

---------

Co-authored-by: Huang Xin <chrox.huang@gmail.com>
2026-03-03 17:10:13 +01:00
Huang Xin 0609e828b1 fix(css): unset hardcoded calibre color and background color, closes #3448 (#3457) 2026-03-03 11:06:03 +01:00
Huang Xin 35ade2c855 fix(tts): handle documents without lang attribute or XHTML namespace, closes #3291 (#3456) 2026-03-03 10:35:33 +01:00
Huang Xin b68c14da1f i18n: add translations for Slovenian(sl), closes #3453 (#3455) 2026-03-03 07:20:59 +01:00
Huang Xin 5c41394961 feat(footnote): make popup window size more responsive for longer footnotes, closes #3425 (#3454) 2026-03-03 07:09:34 +01:00
Huang Xin 5685944599 fix(css): unset padding and margin in body, closes #3441 (#3452) 2026-03-03 04:52:21 +01:00
Huang Xin 94e761f681 fix(txt): more robust chapter extractor for TXT (#3446) 2026-03-02 18:20:50 +01:00
Huang Xin 7f636a2072 fix(toc): correct TOC grouping to prevent unnecessary nested layers (#3445) 2026-03-02 16:44:02 +01:00
Huang Xin 480b8b98a3 fix(css): properly constrain the max width and height of images, closes #3432 (#3444) 2026-03-02 16:20:01 +01:00
Huang Xin d1fb67316f compat(css): support duokan-page-fullscreen in spine to display cover image in fullscreen, closes #3424 (#3443) 2026-03-02 15:16:04 +01:00
Huang Xin f0e9ddd2ae feat(font): support loading custom font if embedded fonts in EPUBs are missing (#3439) 2026-03-02 09:18:18 +01:00
IGCFck b16a4445ae fix(opds): handle non-ASCII login details (#3436) 2026-03-02 07:01:47 +08:00
StepanSad 515d47e64d Update translation.json (#3423)
* Update translation.json

* Update translation for pages left in chapter

---------

Co-authored-by: Huang Xin <chrox.huang@gmail.com>
2026-03-01 17:10:07 +01:00
Huang Xin 3fd78c4ed8 fix(gallery): support displaying svg image in image gallery mode, closes #3427 (#3435) 2026-03-01 17:08:51 +01:00
bfcs 152a95941a fix(translation): handle missing Cloudflare context on non-Cloudflare deployments (e.g., Vercel) (#3433)
* fix(translation): handle missing Cloudflare context on non-Cloudflare deployments (e.g., Vercel)

* Simplify error handling for Cloudflare context

---------

Co-authored-by: Huang Xin <chrox.huang@gmail.com>
2026-03-01 16:40:45 +01:00
Huang Xin bf5805910d feat(footnote): add back navigation for footnote popup with link history, closes #3420 (#3434) 2026-03-01 16:30:59 +01:00
Huang Xin 431d14f4a4 fix(layout): respect grid insets for the zoom controller, closes #3426 (#3430) 2026-03-01 09:58:54 +01:00
Huang Xin 0d2e5b7c76 fix(translation): reduce initial layout shift in the translation view, closes #3078 (#3428)
* fix(css): allow overriding the padding of the root html

* fix(translation): reduce initial layout shift in the translation view, closes #3078
2026-03-01 09:12:02 +01:00
bfcs e949476d27 fix(translation): resolve DeepL translation failure with auto-detection (#3412)
* fix(translation): correctly handle DeepL source_lang 'AUTO' by omitting it

* refactor: backward compatibility

---------

Co-authored-by: Huang Xin <chrox.huang@gmail.com>
2026-03-01 06:03:52 +01:00
Huang Xin 0afff573a1 chore: bump dependencies to resolve Dependabot alerts (#3421) 2026-02-28 18:31:33 +01:00
Huang Xin d4bb61f12b fix(layout): responsive layout for OPDS catalogs and download button (#3418) 2026-02-28 17:07:20 +01:00
Huang Xin 09c45b4615 fix(linux): avoid transitions API on WebKitGTK on Linux (#3417) 2026-02-28 16:35:39 +01:00
Huang Xin d8eef87bf0 chore(agents): add AGENTS.md for readest-app (#3415) 2026-02-28 15:30:31 +01:00
Huang Xin 66d2fdf999 release: version 0.9.101 (#3410) 2026-02-28 09:50:23 +01:00
Huang Xin d8e0ceeff1 fix(annotator): add page number in highlight export to Readwise (#3409) 2026-02-28 09:45:09 +01:00
Huang Xin f3ad97b989 fix(opds): add missing book description in OPDS feed (#3408) 2026-02-28 09:30:56 +01:00
Huang Xin 1bb49ab023 fix(tts): dispose of the TTS view when shutting down the TTS controller, closes #3400 (#3406) 2026-02-28 08:58:02 +01:00
bfcs f9a0b39586 fix: respect fixed translation quota in UI stats and DeepL provider (#3404) 2026-02-28 08:10:31 +01:00
Huang Xin c533da498d feat(annotator): add page number for annotations, closes #3082 (#3405)
* feat(annotator): add page number in annotation

* feat: add page number for annotations, closes #3082
2026-02-28 08:08:56 +01:00
Huang Xin b50bc0b854 fix: make touchpad scrolling respect the system’s natural scrolling settings, closes #3127 (#3398) 2026-02-27 07:24:49 +01:00
Huang Xin 96c465931c fix(toc): fix phantom subchapter TOC item (#3397) 2026-02-27 07:23:12 +01:00
Huang Xin 2bd54ac236 fix(tts): also show highlight when navigating in paused mode and improve abbreviations processing (#3396)
Closes #3317.
2026-02-27 06:46:27 +01:00
Huang Xin 6ad549d13c fix(iOS): correct sidebar insets on iPad and resolve occasional stale safe area inset on iOS (#3395) 2026-02-27 05:43:45 +01:00
Blyrium 67249370c9 fix(font): fix generic font family keywords bypassing user font settings (#3394)
* Fix generic font families resolving incorrectly

Replace `serif`, `sans-serif`, and `monospace` keywords in epub stylesheets with their corresponding CSS variables (`var(--serif)`, `var(--sans-serif)`, `var(--monospace)`), ensuring the user's configured fonts are always used.
Fixes https://github.com/readest/readest/issues/3334

* Got rid of the lookbehind

But now we're using a placeholder.
2026-02-27 08:55:24 +08:00
Huang Xin fe3ab011ca fix(tts): set document lang attribute when missing or invalid for TTS, closes #3291 (#3393) 2026-02-26 17:17:51 +01:00
Huang Xin 6cb4278b98 fix: fixed all progress at the last page of the book, closes #3383 (#3391) 2026-02-26 14:01:42 +01:00
Huang Xin 51468862a2 fix(ui): show progress 100% at the last page, closes #3383 (#3390) 2026-02-26 11:08:21 +01:00
Huang Xin b3a44d066f fix(layout): enlarge hit area for slider input on iOS, closes #3382 (#3389) 2026-02-26 09:26:28 +01:00
Huang Xin 68d4538d40 fix(layout): float the annotation navigation bar, closes #3386 (#3387) 2026-02-26 06:56:29 +01:00
Roy Zhu 80105af839 fix(txt): stabilize iOS large TXT import and decode picker paths (#3320)
* fix: add missing TXT worker files for large import path

* fix(ios): decode import paths and stabilize large TXT conversion

* style(txt): run prettier on TXT conversion files

* fix(txt): restore chunk iterator API and stream cancel behavior

* fix(txt): avoid unused private segment iterator

* chore: clean up log for unit tests

---------

Co-authored-by: Huang Xin <chrox.huang@gmail.com>
2026-02-26 05:30:13 +01:00
Huang Xin 3904c1e8e7 perf: use GPU-accelerated scroll for smoother paging animation (#3385) 2026-02-26 03:39:37 +01:00
Alan 99be6c58e2 fix: close searchbar when adding a new annotation (#3384) 2026-02-26 09:20:27 +08:00
Huang Xin 664cc772d0 fix: more sensitive paging without snapping animation, closes #3310 (#3381) 2026-02-25 17:47:55 +01:00
Huang Xin 21208adbcf compat: add target class to hide import icon in the bookshelf, closes #3376 (#3380) 2026-02-25 14:13:10 +01:00
Huang Xin 70eb59e2d6 compat(css): only override img background when overriding book color, closes #3377 (#3379) 2026-02-25 11:43:02 +01:00
Huang Xin 3e7b57282e fix: delay context menu to prevent broken input loop on macOS, closes #3324 (#3378)
When a native context menu is triggered via right-click, the menu.popup()
call immediately takes control and disrupts the browser's pointer event
flow. This prevents the pointerUp event from being delivered, leaving the
input event loop in an inconsistent state where the first tap after
dismissing the menu is not captured.

The fix adds a 100ms delay before showing the context menu, allowing the
browser's input loop to complete the pointerUp event for the right-click
before the native menu interferes.
2026-02-25 11:22:25 +01:00
Huang Xin 534582b125 compat(css): unset none user-select in some EPUBs, closes #3370 (#3374) 2026-02-25 07:10:06 +01:00
Huang Xin 4465e6986e chore: add husky pre-commit and pre-push hooks (#3372) 2026-02-25 06:39:11 +01:00
Blyrium a535f6419e fix: allow CSS targeting of the "NUMBER pages left in chapter" label via <Trans> component (#3368)
* Allow users to change the "remaining pages" label

Makes the remaining page number and the label text that comes with it targetable with CSS.
Solves https://github.com/readest/readest/issues/3343.

* Fixed formatting
2026-02-25 06:11:45 +01:00
Huang Xin 65da9c1d47 compat(webview): compat with older webview for iterating gamepads (#3366) 2026-02-24 16:59:52 +01:00
Huang Xin 5f71fd9e47 fix(css): override inline image background color only when overriding book color, closes #3316 (#3365) 2026-02-24 16:04:28 +01:00
Huang Xin dcf75e07d1 feat(translator): add Khmer in translator target languages, closes #3323 (#3364) 2026-02-24 15:51:50 +01:00
Huang Xin 79ba9b3818 compat(css): unset font-family for body when set to serif or sans-serif, closes #3334 (#3363) 2026-02-24 15:18:12 +01:00
Huang Xin 128b238bcb feat: add directional view transitions with scroll preservation in library view, closes #3357 (#3362) 2026-02-24 14:56:45 +01:00
Mohammed Efaz e26f7e6a2c fix: empty paragraphs not skipped in paragraph mode (#3361)
* fix(paragraph): skip empty paragraph ranges

* test(paragraph): cover empty paragraph filtering
2026-02-24 09:05:35 +01:00
Huang Xin 4c5ff59bcf fix(layout): also scale table with parent width, closes #3284 (#3359) 2026-02-24 08:11:45 +01:00
Huang Xin 99b2a34bd2 compat(layout): fix insane block display for tables, closes #3351 (#3358) 2026-02-24 07:26:03 +01:00
Huang Xin 40f3268ef3 fix(layout): consistent padding and radius for the dialog header, closes #3352 (#3356) 2026-02-24 06:55:47 +01:00
Huang Xin 4dac0850c5 fix: add classes for progress info labels, closes #3343 (#3353) 2026-02-24 05:24:01 +01:00
Huang Xin 118538ba35 fix(epub): replace background also for scrolled mode, closes #3344 (#3350) 2026-02-24 04:37:44 +01:00
JustADeer 4a92cacd84 fix: changed tagName comparision to localName for case-insensitive in iframeEventHandlers (XHTML xmlns bug fix) (#3349) 2026-02-24 09:21:03 +08:00
Matt Vogel ce53cd2b47 feat: Readwise highlights sync (#3311) 2026-02-24 00:08:59 +08:00
JustADeer b99c1bc19a feat(ui): image viewing mode support (#3328)
* feat(ui): image viewing mode support

* Revert foliate-js submodule pointer to 72fda6a (CI-compatible)

* Fix long-press image viewing in foliateViewer instead of foliate-js and other refactors

* feat: add images navigation buttons and table viewer

---------

Co-authored-by: Huang Xin <chrox.huang@gmail.com>
2026-02-22 17:28:48 +01:00
Aniket Kotal eec2c39f19 feat(docker/podman): self-hosting with docker/podman compose (#3312)
* initial files

* added testing files

* removed unused files

* cleaned additional mounts

* fixed sql init

* removed more unused files

* moved to docker folder

* revert package.json

* gitignore update

* env example comments and compose necessary healthcheck

* ghcr package impl

* updated dockerfile steps  for layer caching

* added development-stage to dockerfile to dev environment

* added documentation on how to use dockerfile and compose.yml

* fixed prettier issues

* fixed image tag

* removed workflow for later
2026-02-18 14:28:10 +01:00
Mohammed Efaz c6ae85484e fix(reader): clamp reading ruler within viewport (#3314) 2026-02-18 14:13:06 +08:00
Huang Xin 7a9f46e93c fix(layout): container layout for dimmed area of reading ruler, closes #3304 (#3313) 2026-02-17 18:34:48 +01:00
Mohammed Efaz b9f6578127 feat: remaining time in TTS mode (#3300)
* feat: add test

* chore: update test

* feat: add translations

* feat: add tts time

* feat: add tts time components

* feat: update test

* feat: fixes and update ui components

* refactor: revert translations and ui
2026-02-14 17:42:37 +01:00
Huang Xin e75a3d254e fix(tts): fixed an issue where starting TTS from the annotation tool did not work, closes #3292 (#3303) 2026-02-14 17:22:46 +01:00
Huang Xin ea15906acf fix(txt): fixed TXT import on platforms that CompressionStream is not working, closes #3255 (#3302) 2026-02-14 23:30:38 +08:00
Mohammed Efaz 15d2784725 fix: highlight in dark mode eink (#3299)
* fix: highlight in dark mode eink

* refactor: deduplicate

* fix: bw eink highlights to use the fg colour so saved highlights are visible

* fix: avoid grayscale color in E-ink mode for better legibility

---------

Co-authored-by: Huang Xin <chrox.huang@gmail.com>
2026-02-14 13:32:52 +01:00
Huang Xin ae3bb9da9a fix(annotator): update highlight style, color and range handlers when showing different annotations, closes #3286 (#3289) 2026-02-13 16:46:44 +01:00
Mohammed Efaz 239b32fcc2 fix: reader ruler disabled in scroll mode (#3288) 2026-02-13 16:35:25 +01:00
Huang Xin f64739419b release: version 0.9.100 (#3279) 2026-02-12 18:08:30 +01:00
Huang Xin af8f036ca3 fix(annotator): apply custom highlight colors in sidebar, closes #3273 (#3278) 2026-02-12 17:43:11 +01:00
Huang Xin 24a87508c6 fix(layout): respect image dimension attribute, closes #3274 (#3277) 2026-02-12 16:41:57 +01:00
Huang Xin 548d50a882 fix(tts): navigate to follow the current TTS location for the next section, closes #3198 (#3276) 2026-02-12 16:13:41 +01:00
Huang Xin 1e81ab5205 doc: update sponsor link in README (#3275)
Updated the TestMu AI sponsor link in the README.
2026-02-12 14:10:54 +01:00
Huang Xin a47a480aa2 fix(layout): persistent location for the sidebar toggle, closes #3193 (#3269) 2026-02-11 16:29:26 +01:00
Huang Xin 411f9e236d feat(metadata): support parsing series info from calibre exported EPUBs, closes #3259 (#3267) 2026-02-11 15:39:48 +01:00
Huang Xin 950bbd0821 fix: expose srcdoc also for html sections, closes #3264 (#3266) 2026-02-11 13:15:56 +01:00
Huang Xin 2824e50b51 feat(annotator): snap text selection to word boundary, closes #3234 (#3265) 2026-02-11 12:53:23 +01:00
Huang Xin 129720c916 fix(eink): more legibility for the dictionary and wikipedia popups on Eink devices, closes #3258 (#3262) 2026-02-11 10:22:05 +01:00
Huang Xin fd3533dba1 feat(annotator): add a loupe when adjusting text selection range, closes #3002 (#3261) 2026-02-11 10:13:22 +01:00
Huang Xin 920032155b fix(tts): fix paused tts will still read to the end of the current paragraph, closes #3244 (#3256) 2026-02-11 03:06:12 +01:00
Huang Xin 226bf7033e fix(layout): fixed some layout issues for RSVP, closes #3199 (#3254) 2026-02-10 17:38:33 +01:00
Huang Xin 9444be7fcc feat(annotator): rounded highlight style for horizontal and vertical layout, closes #3208 (#3252) 2026-02-10 16:13:53 +01:00
Huang Xin c9a69a922b fix(layout): workaround for hardcoded table layout, closes #3205 (#3251) 2026-02-10 15:19:36 +01:00
Huang Xin 9a11b05833 fix: don't cache section content when updating subitems, closes #3242 and closes #3206 (#3250) 2026-02-10 14:29:57 +01:00
Huang Xin 6e603ee38f compat: compatibility with older webview on Android, closes #3245 (#3249) 2026-02-10 13:47:25 +01:00
Huang Xin 03fd6e2e6f feat(metadata): collapsible sections in book details, closes #3217 (#3243) 2026-02-09 17:56:34 +01:00
Huang Xin 7dd11c0fb0 fix: fixed docker build by including some dist files in docker image, closes #3233 (#3241) 2026-02-09 15:54:45 +01:00
Huang Xin 968597e52c doc: update toubleshooting and features list, closes #3224 (#3232) 2026-02-09 08:36:16 +01:00
Huang Xin a534050b19 fix(progress): show physical left pages instead of estimated ones, closes #3213 and closes #3200 (#3231) 2026-02-09 05:44:00 +01:00
Huang Xin 0be828fd66 feat(l10n): format sync date time with current locale, closes #3219 (#3230) 2026-02-09 03:09:52 +01:00
Huang Xin c6d4e2bdd6 fix(android): fixed some annotation tools not responsive to tap on Android, closes #3225 (#3229) 2026-02-09 02:26:57 +01:00
Mohammed Efaz bf72ab86cd fix: status button eink compatible (#3223) 2026-02-08 12:28:31 +01:00
Mohammed Efaz fb49ddf484 feat: back button dictionary (#3220)
* feat: add back button with history in dictionry/wikitionary

* fix: flicker back button
2026-02-08 12:27:05 +01:00
Huang Xin d8817a88b9 i18n: add support for Hebrew, closes #3216 (#3218) 2026-02-08 06:55:53 +01:00
Mohammed Efaz 475becafe3 fix: para mode nav buttons issue in eink (#3212) 2026-02-07 17:22:24 +01:00
Mohammed Efaz df165576e6 fix: reader ruler ubresponsive in android (#3210) 2026-02-07 23:16:28 +08:00
Huang Xin 051f2e5b13 fix(layout): show navigation buttons with higher z-index, closes #3201 (#3204) 2026-02-07 10:04:05 +01:00
Huang Xin 83f0d135c5 release: version 0.9.99 (#3190) 2026-02-06 18:51:03 +01:00
Huang Xin 1ed84a3256 feat(tts): navigation in TTS mode with back to current button, closes #3100 (#3189) 2026-02-06 18:26:14 +01:00
Huang Xin 9d6394fe2b fix(layout): fixed page navigation buttons have higher z-index than TTS control button (#3184) 2026-02-06 11:00:56 +01:00
Huang Xin fe9603ffb8 fix(a11y): updating progress info when navigating with screen readers (#3182)
Also add some missing button labels for screen readers
2026-02-06 08:43:03 +01:00
boludo00 681e065ac4 fix(rsvp): fix position restoration and keyboard handling (#3150)
* feat: add RSVP speed reading feature

Implements Rapid Serial Visual Presentation (RSVP) speed reading mode:

- Display words one at a time with ORP (Optimal Recognition Point) highlighting
- Adjustable WPM speed (100-1000) with keyboard/button controls
- Punctuation pause settings (25-200ms)
- Progress tracking with chapter navigation
- Context panel showing surrounding text when paused
- Keyboard shortcuts (Space, Escape, arrows) and touch gestures
- Chapter selector for quick navigation
- Respects current theme colors
- Persists settings (WPM, pause duration, position) per book

New files:
- services/rsvp/RSVPController.ts - Main controller with playback logic
- services/rsvp/types.ts - TypeScript interfaces
- components/rsvp/RSVPOverlay.tsx - Full-screen RSVP reader UI
- components/rsvp/RSVPControl.tsx - Integration component
- components/rsvp/RSVPStartDialog.tsx - Start position selection

Closes #3111

* test(rsvp): add Playwright e2e tests for RSVP feature

- Set up Playwright test infrastructure with config
- Add comprehensive e2e tests for RSVP speed reading:
  - Opening RSVP from View menu
  - Start dialog options
  - Play/pause toggle
  - Speed adjustment
  - Skip navigation
  - Keyboard shortcuts
  - Progress bar
  - Chapter navigation
  - Accessibility tests
- Add test data attributes and ARIA labels to RSVPOverlay
- Add test scripts to package.json

* refactor: remove test files, unsure of use case for now

* chore: remove Playwright e2e test scripts from package.json

- Deleted e2e test scripts related to Playwright from package.json as they are no longer needed.
- Removed Playwright as a dev dependency to streamline the project.

* chore: sync pnpm-lock.yaml after removing @playwright/test

* fix: lint errors and timezone-aware date formatting

* fix(rsvp): restore correct position when exiting RSVP mode

Fix bug where exiting RSVP after changing speed would navigate to wrong
position (several pages backwards). The issue was that Range objects
become stale if the document re-renders, and the position recovery used
global word index instead of per-document index.

- Add docWordIndex to track word position within each document
- Add docTotalWords to RsvpStopPosition for accurate recovery
- Validate Range before using it, recreate from current DOM if stale
- Use same word extraction logic as RSVPController for consistency

* fix(rsvp): prevent arrow keys from changing chapter dropdown

Arrow keys during RSVP mode were inadvertently navigating the chapter
dropdown instead of controlling RSVP speed. Fixed by using capture phase
for keyboard events and stopping propagation to prevent native elements
from receiving the events.

* chore: remove playwright-mcp screenshots from repo

* chore: update gitignore

* fix(i18n): wrap all RSVP overlay strings with translation function

Restore missing translation wrappers for all user-facing strings:
- Close RSVP button labels and tooltips
- Select Chapter dropdown
- WPM display
- Context panel header
- Ready state text
- Chapter Progress label
- Words count and time remaining
- Reading progress slider
- Pause/punctuation label
- Skip back/forward buttons
- Play/Pause button
- Speed control buttons

* fix(rsvp): use CFI-based position tracking for accurate page positioning

- Add CFI generation for each extracted word during RSVP processing
- Implement CFI path and character offset comparison for precise position matching
- Update startFromCurrentPosition to use CFI matching instead of word index
- Add extractFullDocPathWithOffset to handle CFI character offsets
- Simplify RSVPControl by removing unused helper functions
- Remove word index from RsvpPosition in favor of CFI-only tracking

* fix(ViewMenu): re-enable Speed Reading Mode in production environment

* refactor(ViewMenu, RSVPController): clean up code formatting and improve readability

* refactor(ViewMenu): remove unused IoSpeedometer import

* refactor(RSVPController): streamline CFI handling and improve section comparison

- Removed redundant logic and re-using CFI util functions for CFI operations

* refactor(RsvpPosition): remove legacy wordIndex field for cleaner type definition
2026-02-05 18:19:13 +01:00
Huang Xin f64fc5723e fix(a11y): double tap to focus on current paragraph and update page info with TalkBack, closes #2276 (#3179) 2026-02-05 18:18:45 +01:00
Huang Xin a9a1dc8e70 fix(layout): fixed regression of vertical alignment, closes #3012 (#3178) 2026-02-05 17:46:47 +01:00
Huang Xin b1a1e35790 fix(layout): move sync options inside of account section in settings menu (#3176) 2026-02-05 10:08:17 +01:00
Huang Xin 3c538c3746 feat(sync): add manual books sync in the main menu (#3175) 2026-02-05 08:26:33 +01:00
Huang Xin 9834bd57cf feat(toc): add page number for nested TOC items, closes #2953 (#3174) 2026-02-05 08:19:18 +01:00
Huang Xin b89171a4d8 compat(iOS): disable native CompressionStream/DecompressionStream API in zip.js for iOS 15.x (#3170) 2026-02-04 13:18:36 +01:00
Huang Xin 92116e7455 fix(bookshelf): merge groups and ungrouped books then sort them together (#3169) 2026-02-04 12:32:29 +01:00
Huang Xin 8dfab2c963 feat(a11y): always show page navigation buttons in reader page for screen readers, closes #3036 (#3167) 2026-02-04 10:07:37 +01:00
Huang Xin 9f261f12e9 fix(kosync): don't show conflict prompt when progress diff is less than 0.01% (usually from the same device) (#3166)
This should close #3137.
2026-02-04 08:28:15 +01:00
Huang Xin e74615ac1d feat(export): add an option to export annotations as plain text, closes #3147 (#3165) 2026-02-04 07:50:40 +01:00
Huang Xin 3b350d6945 fix(layout): responsive select mode actions on smaller screens (#3164) 2026-02-04 07:04:29 +01:00
Huang Xin 9e0e3fde7d fix(layout): fix aligment of options in settings menu, closes #3151 (#3163) 2026-02-04 06:42:14 +01:00
Adam Charron 52c49ddef1 Add enhanced grouping and sorting functionality to the library view (#3146)
* feat(library): implement grouping of books by series or author

- Added functionality to group books in the library by series or author.
- Introduced new utility functions for creating book groups and parsing author strings.
- Updated the LibraryPageContent to track the current group for navigation.
- Created a new GroupHeader component to display the current group context.
- Enhanced the Bookshelf component to support displaying grouped books.
- Updated settings to include grouping options in the view menu.

New files:
- src/__tests__/utils/libraryUtils.test.ts - Unit tests for new utility functions.
- src/app/library/components/GroupHeader.tsx - Component for displaying group context.
- src/app/library/utils/libraryUtils.ts - Utility functions for grouping and parsing authors.

* Use group by and sort by constants, and split sort/group by menus into their own elements

* Format code with autoformatter

* Remove any casting from tests

* Translate missing strings

* refactor: cleaner layout with collapsible view menu options

---------

Co-authored-by: Huang Xin <chrox.huang@gmail.com>
2026-02-03 14:51:50 +01:00
Huang Xin e3d52891fb fix(a11y): add missing aria labels and fix iOS safe insets (#3149) 2026-02-03 12:50:24 +01:00
Huang Xin 9cd88fe839 fix(progress): show correct progress info for subsections in FB2, closes #3136 (#3145) 2026-02-02 17:53:26 +01:00
Huang Xin fca3917a12 fix(toc): prevent unintentional scrolling in the TOC, closes #3124 (#3144) 2026-02-02 14:28:11 +01:00
Adam Charron c90de6967b Change setup command for vendors in CONTRIBUTING.md (#3139)
Updated instructions for setting up dependencies in the project.
2026-02-02 04:41:54 +01:00
boludo00 bbbd378f9b feat: rsvp speed reading (#3121)
* feat: add RSVP speed reading feature

Implements Rapid Serial Visual Presentation (RSVP) speed reading mode:

- Display words one at a time with ORP (Optimal Recognition Point) highlighting
- Adjustable WPM speed (100-1000) with keyboard/button controls
- Punctuation pause settings (25-200ms)
- Progress tracking with chapter navigation
- Context panel showing surrounding text when paused
- Keyboard shortcuts (Space, Escape, arrows) and touch gestures
- Chapter selector for quick navigation
- Respects current theme colors
- Persists settings (WPM, pause duration, position) per book

New files:
- services/rsvp/RSVPController.ts - Main controller with playback logic
- services/rsvp/types.ts - TypeScript interfaces
- components/rsvp/RSVPOverlay.tsx - Full-screen RSVP reader UI
- components/rsvp/RSVPControl.tsx - Integration component
- components/rsvp/RSVPStartDialog.tsx - Start position selection

Closes #3111

* fix(rsvp): use portal to fix overlay stacking context issue

- Render RSVP overlay and start dialog via React Portal to document.body
- This ensures the overlay appears above all other content regardless of
  parent component CSS transforms or stacking contexts
- Add fallback colors for theme values to ensure solid background

* fix(rsvp): improve UX with progress sync, sentence highlight, and better dialogs

- Fix start dialog transparency by using solid opaque background with proper
  fallback colors for both light and dark modes
- Increase context words from 50 to 100 words before/after current word
- Add progress sync on RSVP exit - navigates reader to the last word position
- Add temporary bright green sentence underline on exit (5 second duration)
  to easily locate where reading left off
- Helper function to expand word range to full sentence boundaries

* fix(rsvp): store full BookNote for proper annotation removal

The addAnnotation API requires the full BookNote object (including CFI)
when removing annotations, not just the ID. Changed tempHighlightIdRef
to tempHighlightRef to store the complete BookNote object.

* test(rsvp): add Playwright e2e tests for RSVP feature

- Set up Playwright test infrastructure with config
- Add comprehensive e2e tests for RSVP speed reading:
  - Opening RSVP from View menu
  - Start dialog options
  - Play/pause toggle
  - Speed adjustment
  - Skip navigation
  - Keyboard shortcuts
  - Progress bar
  - Chapter navigation
  - Accessibility tests
- Add test data attributes and ARIA labels to RSVPOverlay
- Add test scripts to package.json

* fix(rsvp): clarify start dialog option labels

Update the RSVP start dialog to use clearer language:
- "From Beginning" → "From Chapter Start" (since it starts from chapter beginning)
- "From Current Position" → "From Current Page" (starts from visible page)

* fix(rsvp): use correct theme colors from themeCode

The RSVP components were using incorrect palette key names (camelCase
like `base100` instead of hyphenated like `base-100`), causing the
fallback colors to always be used instead of the reader's actual theme.

Fix by using themeCode.bg, themeCode.fg, and themeCode.primary directly,
which are already resolved from the palette with correct keys.

* fix(rsvp): use theme accent color for sentence underline and persist until page change

- Change underline color from hardcoded green (#22c55e) to theme accent
  color (themeCode.primary), matching the ORP focal point highlight
- Remove 5-second timeout that auto-removed the underline
- Add cleanup on page navigation, new RSVP session start, and unmount
- Add removeRsvpHighlight helper function for consistent cleanup

* fix(rsvp): transition to next chapter when reaching end of section

When RSVP reached the end of a chapter, it would restart the current
chapter instead of moving to the next one. This happened because RSVP
extracts ALL words from the current section via renderer.getContents(),
so when words run out, the entire chapter is done.

- Always use view.renderer.nextSection() when RSVP exhausts words
- This moves to the next chapter instead of staying in the current one

* refactor: remove test files, unsure of use case for now

* chore: remove Playwright e2e test scripts from package.json

- Deleted e2e test scripts related to Playwright from package.json as they are no longer needed.
- Removed Playwright as a dev dependency to streamline the project.

* fix(rsvp): ensure CFI retrieval occurs before navigation

- Updated comments to clarify the necessity of obtaining CFI for both the navigation and sentence highlight before invoking the goTo() method, as it may re-render the document and invalidate Range objects.
- Introduced a new variable for sentence text to enhance readability and maintainability of the code.

* chore: sync pnpm-lock.yaml after removing @playwright/test

* style: format RSVP components

* fix: lint errors and timezone-aware date formatting

* i18n: support CJK text and add translations

---------

Co-authored-by: Huang Xin <chrox.huang@gmail.com>
2026-02-01 18:22:24 +01:00
Huang Xin 9f7147f8f8 fix(layout): fix z index for dropdown (#3135) 2026-02-01 16:36:03 +01:00
Huang Xin 8a468a6d1f fix(opds): more robust parsing for authors in metadata, closes #3120 (#3134) 2026-02-01 15:57:10 +01:00
Huang Xin c848a319ff fix(layout): fix centered dropdown menu (#3133) 2026-02-01 15:52:51 +01:00
Huang Xin 5cc80db438 feat(gesture): support two finger swipe left/right to paginate on trackpad, closes #3127 (#3132) 2026-02-01 09:37:13 +01:00
Huang Xin 94930baa1b feat(sync): more intuitive pull down to sync gesture and toast info, closes #3128 (#3131) 2026-02-01 09:23:24 +01:00
Huang Xin 1294ef90c1 fix(macOS): fix traffic lights position and visibility on macOS, closes #3129 (#3130) 2026-02-01 05:38:40 +01:00
Huang Xin 570598520f fix(a11y): fix accessibility in dropdown menus for TalkBack, closes #3035 (#3126) 2026-01-31 19:05:56 +01:00
Huang Xin 7b4fc91994 fix(layout): fixed layout of book reading progress and reader footer bar (#3125) 2026-01-31 18:16:17 +01:00
Huang Xin fb387c2384 fix(layout): fixed layout of book reading progress and reader footer bar (#3117) 2026-01-30 06:54:20 +01:00
Mohammed Efaz 4ce1ebe477 feat: paragraph by paragraph reading mode (#3096)
* feat: add index

* feat: add bottom nav bar

* feat: add paragraph iterator

* feat: add para mode shortcut

* feat: add paragraph control into foliateviewer

* feat: add paragraph mode toggle to view menu

* feat: add paragraph bar for navigation controls

* feat: add paragraph control wrapper component

* feat: add pargraph overlay for the focused display

* feat: integrate paragraph mode into keyboard navigation

* feat: add paragraph mode state management hook

* feat: add paragraph mode type definition

* feat: add default paragraph mode config

* fix: replace previous storage system with sytem one and fix sync issues

* fix: format
2026-01-30 05:24:23 +01:00
Huang Xin c1460f4b85 fix(css): mix blend only inline images, closes #3112 (#3116) 2026-01-30 05:17:16 +01:00
Huang Xin bc94f3f790 refactor: responsive SetStatusAlert on smaller screens (#3110) 2026-01-29 15:53:20 +01:00
Huang Xin c8c761b017 fix(sync): escape unsafe character in filename (#3109) 2026-01-29 15:13:02 +01:00
Huang Xin bdb25999c9 fix(sync): more robust books sync for stale library, closes #3099 (#3108) 2026-01-29 14:05:58 +01:00
Huang Xin c58e172a54 fix(bookshelf): don't show badge or progress for unread book, closes #3103 (#3107) 2026-01-29 12:48:21 +01:00
Huang Xin 160efcd37b chore: bump next.js to the latest version (#3106) 2026-01-29 12:29:17 +01:00
Huang Xin d7470f4139 fix(iOS): disable online catalogs on iOS from appstore channel (#3102) 2026-01-29 05:52:59 +01:00
Mohammed Efaz 01b4e3530d feat: toggle finished manually (#3091) 2026-01-28 06:26:57 +01:00
Huang Xin 09c62d442f fix(css): no default mix blend mode for hr, closes #3086 (#3093) 2026-01-27 15:03:17 +01:00
Huang Xin d62ad60ce8 chore: bump opennextjs and wrangler to the latest versions (#3092) 2026-01-27 14:57:28 +01:00
Jair Goh 8cd34c8aaa feat(assistant): Add embedding model option to AIPanel (#3090)
* Add embedding model option to AIPanel

* Minor code reformatting

* Edit tests to account for embedding model

* Minor reformatting
2026-01-27 14:28:18 +01:00
Mohammed Efaz a52d9e3a2b feat: add fuzzy search for searching across all settings (#3085)
* feat: enable linking to settings items

* feat: integrate command palette with global state and styles

* feat: add command pallete ui and fuzzy search

* feat: add command palette with global state and styles

* feat: add command registry and search dependencies

* fix: open command palette with shortcuts in reader page

---------

Co-authored-by: Huang Xin <chrox.huang@gmail.com>
2026-01-26 18:32:04 +01:00
Huang Xin 64a1ea531a fix(bookshelf): fixed conflicts of group names with common prefix (#3084) 2026-01-26 13:38:01 +01:00
Mohammed Efaz 19f9f5ea24 feat: auto position reader ruler to top of page and some fixes (#3075)
* fix(settings): ensure latest config is used when saving view settings

* fix: reader ruler appearing before other book reading contents

* feat(reader): dispatch reader-closing event during book close

* feat(reader) add auto reposition to top when changing pages and position persistence on book close

* fix(reader): add rtl prop to ReadingRuler component

* fix(reader): handle vertical ltr writing mode in ruler auto positioning

* chore: revert redundant changes to settings.ts

* refactor: remove redundant reader-closing

* refactor: use store progress for ruler positioning with throttled saving
2026-01-26 08:38:06 +01:00
Huang Xin ffc51e75de feat(ruler): support vertical ruler and transparent ruler (#3076) 2026-01-25 20:15:47 +01:00
Huang Xin 2100071991 feat(eink): support color E-ink mode to display more colors, closes #3037 (#3074) 2026-01-25 17:44:41 +01:00
Huang Xin 563d3478ba feat(gamepad): support gamepad input to paginate, closes #2432 (#3073) 2026-01-25 16:32:05 +01:00
Huang Xin 2c54e9ae2f feat(updater): in-app updater for AppImage (#3072) 2026-01-25 14:45:37 +01:00
Huang Xin c2eb2e2fcc doc: update donation link via Stripe (#3071) 2026-01-25 11:24:10 +01:00
Huang Xin e592f40429 layout: don't show upload icon if auto upload is disabled for cleaner UI (#3068) 2026-01-25 09:22:30 +01:00
Huang Xin 4bd7cfe57c chore: fix dockerfile (#3067) 2026-01-25 08:51:40 +01:00
Mohammed Efaz c31ece6742 feat: more highlight colours (#3062)
* feat(highlight): extend types and constants for custom hex colours

* feat(highlight): update colour to support hex strings

* refactor(annotator): update component to accept hex colours

* feat(highlight): add colour picker with max 4 custom colors

* feat(settings): add custom colour editor with limit

* refactor: custom highlight colors can only be modified in settings

---------

Co-authored-by: Huang Xin <chrox.huang@gmail.com>
2026-01-25 07:52:29 +01:00
Mohammed Efaz aba9e87fc1 feat: line highlight to guide reading (reading ruler) (#3063)
* feat: add increment decrement numbers for opacity

* feat: add reading ruler colours

* feat: add reading ruler config to book config

* feat: reading Ruler settings panel (in layout section)

* feat: draggable reading ruler

* feat: reading ruler in reader view
2026-01-25 06:48:13 +01:00
Huang Xin 2c4df601d8 refactor: rename components/ui to components/primitives (#3064)
Move Radix/shadcn-based UI primitives into a dedicated
`components/primitives` directory to better reflect
their low-level, composable nature.
Imports remain ergonomic via an alias to `components/ui`.
App-level components should continue to use PascalCase filenames.
2026-01-25 06:41:18 +01:00
Huang Xin 8bfc90a5b0 chore: fix nunjucks bundling (#3061) 2026-01-24 18:20:15 +01:00
Mohammed Efaz 8bea05478a feat(ai): AI reading assistant phase 2.1 (minor UI/UX updates) (#3059)
* refactor(ai): remove chat icons from conversation list items

* feat(ai): add scroll-to-bottom button with animations and dynamic spacer

* fix(ai): align connection status icon with text in settings

* feat(ai): scroll to bottom on chat panel open and improve scroll behavior

* feat(ai): add loading overlay with blur transition for chat history

* feat(ai): pass loading state through component chain for history loading
2026-01-24 17:57:00 +01:00
Huang Xin 42fee90a27 feat(annotation): export annotations with style and color fields in annotation template, closes #1734 (#3060) 2026-01-24 17:44:40 +01:00
Huang Xin 45e57c3943 chore: update wrangler and next config (#3058) 2026-01-24 14:36:04 +01:00
Huang Xin 51b0b8483f chore: less build concurrency on cloudflare to get rid of build container OOM (#3056) 2026-01-24 13:27:11 +01:00
Huang Xin cbf7a501b7 chore: pin pnpm version for vercel and cloudflare deployment (#3055) 2026-01-24 12:35:03 +01:00
Huang Xin edfcb75ba5 chore: refresh pnpm lockfile (#3054) 2026-01-24 12:08:52 +01:00
Huang Xin ce76843ccc chore: fix patched dependencies (#3053) 2026-01-24 11:52:28 +01:00
Mohammed Efaz 5bbc5ceccc feat(ai): AI reading assistant phase 2 (#3023)
* feat(ai): add dependencies

* chore: bump zod version to default version

* feat(ai): define types and model constants

* feat(ai): ollama provider for local LLM

* feat(ai): implement openrouter provider for cloud models

* feat(settings): register ai settings panel in global dialog

* refactor(ai): expose provider factory and service layer entry point

* test(ai): add unit tests for the providers

* test(ai): add unit tests for the providers

* feat(ai): settings panel for ai configurations

* refactor(ai): rewrite aipanel with autosave and greyed out disabled state

* fix: remove unused onClose prop from aipanel

* test(ai): update mock data

* refactor(ai): remove models

* refactor: use centralised defaults in system defaults

* chore(ai): remove comments

* fix(ai): merge default ai settings on load to prevent undefined values

* refactor(ai): rewrite settings panel with autosave and model input

* feat(ai): add ai tab with simplified highlighting

* feat(sidebar): render AIAssistant for ai tab

* feat(ai): add chat UI

* feat(ai); add chat service with RAG context

* feat(ai): temp debug logger

* feat(ai): add RAG service

* feat(ai): add text chunking utility

* feat(ai): add structured method

* feat(ai): add chatstructured method

* feat(ai): add rag types nd structured output schema

* feat(ai): add aistore, indexdb, bm25

* fix: update lock file

* feat(ai): update types for AI SDK v5

* feat(ai): add placeholder gateway model constants

* refactor(ai): update OllamaProvider for AI SDK

* feat(ai): add native gateway provider

* refactor(ai): update provider exports

* refactor(ai): use streamText from AI SDK

* refactor(ai): use embed from AI sdk

* refactor(ai): update provider factory exports

* feat(ai): add AI Elements and shadcn components

* config: add shadcn component config

* deps: add AI SDK and AI Elements dependencies

* config: add ai packages to transpilePackages

* refactor(ai): remove OpenRouterProvider and old tests

* feat(ai):add assistant-ui components

* feat(ai): add TauriChatAdapter for assistant-ui runtime

* refactor(ai): remove ai-elements components

* dep(ai): install assistant-ui and update next config

* chore(ai): export adapters from service index

* feat(ui): enhance ui components for assistant integration

* feat(settings): migrate ai settings to gateway

* feat(sidebar): integrate assistant-ui

* feat: add ai settings toggle to sidebar content

* feat: conditionally show ai tab in sidebar navigation

* feat: update ai model constants for cheaper options

* feat: add gateway provider with proxied embedding

* feat: add timeouts to ollama provider health checks

* feat: add retry logic to rag service embeddings

* feat: add error recovery to ai store

* feat: add ai feature tests

* feat: add ai api endpoints

* feat: add proxied gateway embedding provider

* feat: add ai runtime utilities

* feat: add ai retry utilities

* feat: add tauri env example template

* feat: add web env example template

* chore: add env

* feat(ai): update models and pricing, remove GLM-4.7-FlashX

* feat(ai): improve system prompt with official headings and no numeric citations

* feat(ai): optimize system prompt for tauri chat

* feat(ui): refine ai chat UI and relocate sources

* feat(ui): update ai settings panel with model pricing and custom model support

* feat(ai): add custom model support to ai settings

* test(ai): update constants tests for removed model

* feat(api): implement ai chat proxy route

* feat(api): implement ai embedding proxy route

* feat(ai): implement ai gateway health check and proxy logic

* feat(ai): simplify proxied embedding provider

* feat(ui): improve markdown text rendering

* feat(ui): add input group component

* test(ai): update ai provider tests

* feat(ai): add pageNumber to text chunk schema

* feat(ai): implement page-based chunking with 1500 char formula

* feat(ai): bump db to v2 and add store reset migration

* feat(ai): transition rag pipeline to page level spoiler filtering

* feat(ai): overhaul readest persona and antijailbreak prompt

* feat(ai): update tauri adapter for page tracking and persona

* chore(ai): export aiStore and logger from core index

* feat(reader): integrate page tracking and manual index reset

* feat(ui): add re-index action and reset logic to chat

* chore: sync pnpm lockfile with ai dependencies

* feat(utils): add browser-safe file utilities for web builds

* refactor(utils): use dynamic tauri fs import to prevent web crashes

* refactor(services): defer osType call to init() for web compatibility

* refactor(services): import RemoteFile from file.web

* refactor(services): import ClosableFile from file.web

* fix(libs): cast Entry to any for getData access

* fix(annotator): cast Overlayer to any for bubble access

* refactor(ai): replace SparklesIcon with BookOpenIcon for index prompt

* test(ai): add pageNumber to TextChunk mocks

* test(ai): fix chunkSection signature in tests

* chore: update files

* fix(ai): prevent useLocalRuntime crash when adapter is null

* refactor: optimize annotator overlay drawing

* feat: stabilize AI assistant runtime and adapter

* refactor: improve document zip loader type safety

* feat: update tauri chat adapter for dynamic options

* fix: restore architecture comments and refine platform properties

* build: update lockfile with assistant-ui patch

* fix(library): patch @assistant-ui/react for runtime initialization

* build: update dependencies in readest-app

* build: update root dependencies and patch configuration

* fix(ai): patch @assistant-ui/react for thread deletion and runtime init

* fix(ai): update assistant-ui patch with dist guards and deletion fallback

* build: sync lockfile with assistant-ui patch updates

* chore(env): update .gitignore by removing .env files from it

* chore(env): update .gitignore by adding .env.local

* chore(env): update .gitignore by adding .env*.local

* fix: restore static osType import

* chore: sync submodules with upstream/main

* refactor: remove redundant file.web module and revert import

* chore: update pnpm-lock.yaml

* refactor: revert guards

* refactor; remove deprecated codes and extract prompts.ts

* refactor(ai): remove unused ragservice exports

* refactor: remove unused ollama and embedding models

* refactor: remove unused type

* test: remove test for the now deleted constants

* refactor: remove unused export

* style: fix ui component formatting

* style: fix core and style file formatting

* test: fix broken ai provider import

* fix: typescript error

* fix: add eslint disable command

* fix(deps): remove unused ai sdk provider util after v6 ai sdk migration

* fix(patch): add lookbehind regex patch

* feat(dep): upgrade vercel ai sdk to v6 and ai-sdk-ollama to v3

* chore: update lockfile for vercel ai sdk v6

* refactor(ai): remove EmbeddingModel generic for ai sdk v6

* refactor(ai): remove EmbeddingModel generic for ai sdk v6

* test(ai): update mock to use embeddingModel

* fix(patch): add lookbehind regex patch for email autolinks in markdown

* refactor(ai): use ai sdk v6 syntax

* fix: prettier formatting

* chore: revert cargo.lock

* fix(ai): update proxied embedding model to v3 spec

* feat(ai): add aiconversation types for chat persistence

* feat(ai): add conversation/message indexeddb and crud operations

* feat(ai): create aiChatStore zustand store for chat state management

* feat(notebook): add notebookactivetab state for Notes/AI

* refactor(ai): refine conversation and message types for persistence

* feat(types): add notebookActiveTab to ReadSettings type

* chore: update deps

* feat: add notebookactive tab default value

* feat: add hook for ai chat

* feat: update left side panel with history/chat icon

* feat: integrate ChatHistoryView into sidebar content

* feat: create UI for managing AI chat history

* feat: implement persistent history with assistant-ui adapter

* feat: create tab navigation component for notes and AI

* feat: add tab navigation and AI assistant view

* feat: update header to display active tab title

* fix: formatting

* feat: remove title and update new chat button

* fix: formatting

* fix: revert tooltip and styling

* feat: implement cross-platform ask dialog bridge

* feat(ai): preserve history during ui clear & use native dialogs

* fix: align notebook navigation height with sidebar tabs

* fix(ai): add missing dependency to handleDeleteConversation hook

* docs: update PROJECT.md with session highlights

* chore: delete projectmd

* chore: update package.json and lock file

* chore: update package.json

* chore: remove patch

* chore: upgrade react types to 19 and show ai features only in development mode for now

---------

Co-authored-by: Huang Xin <chrox.huang@gmail.com>
2026-01-24 11:38:48 +01:00
Huang Xin 224acd68b1 fix(sanitizer): add XHTML11 doc type to recognize nbsp entity, closes #3024 (#3043) 2026-01-23 17:49:06 +01:00
Huang Xin 6c0f1b8b5f fix(annotator): fix instant action on Android (#3042) 2026-01-23 17:18:51 +01:00
Huang Xin 481d8198e9 fix(tts): set playback rate after play only on Linux (#3040) 2026-01-23 12:14:13 +01:00
Huang Xin b1153ba051 fix(sync): correctly update last sync timestamp when the bookshelf has no books yet (#3039) 2026-01-23 04:39:22 +01:00
Huang Xin aad532bfdd fix(sync): hotfix for the initial race condition for books sync (#3038) 2026-01-23 04:32:03 +01:00
Huang Xin a71347c897 fix(layout): more responsive layout on smaller screens, closes #3029 (#3034) 2026-01-22 17:32:34 +01:00
Huang Xin 034f41ca10 fix(shortcuts): prevent system search bar from showing with ctrl+f, closes #3013 (#3033) 2026-01-22 15:59:21 +01:00
Huang Xin 539ad8dea2 fix(layout): correctly constrain the maximum width of fixed-layout tables, closes #3028 and closes #3017 (#3032) 2026-01-22 14:56:35 +01:00
Huang Xin 48920a87bf chore(opds): disable popular online opds catalogs in certain regions on App Store (#3031) 2026-01-22 11:42:16 +01:00
xijibomi-coffee d1d0d2d59c chore: enforce prettier, ignore submodules and vendor files (#3018)
* chore: enforce prettier, ignore submodules and vendor files

* chore: add format check in CI

---------

Co-authored-by: Huang Xin <chrox.huang@gmail.com>
2026-01-21 14:53:02 +01:00
xijibomi-coffee ea811c90c6 feat(ui): switch typography to Inter (#3009) 2026-01-21 13:49:37 +01:00
Jon Volkmar 5cd5b65e83 fix: Conditionally set Cache-Control header to prevent caching in development environments. (#3010) 2026-01-21 03:18:08 +01:00
Huang Xin ea24b5a97a fix(account): redirect to auth page if unauth user is in user page (#3008) 2026-01-20 21:55:53 +01:00
Huang Xin 06d620db83 chore: fixed signing of the portable version for Windows (#3007) 2026-01-21 03:23:39 +08:00
Huang Xin 0c25b85a8a release: version 0.9.98 (#3006) 2026-01-20 18:54:06 +01:00
Huang Xin c536450ab0 fix(fonts): host fonts in Readest CDN for more stable web fonts distribution (#3005) 2026-01-20 18:44:22 +01:00
Huang Xin c83e380c5a chore(doc): use png resources for sponsors logo (#3003) 2026-01-20 07:47:28 +01:00
Huang Xin 894a7551aa chore(doc): update sponsors info (#3002) 2026-01-20 07:42:06 +01:00
xijibomi-coffee f875ba88ac perf: use native walkdir for recursive imports from directory (#2993)
* fix(perf): replace JS recursion with native Rust walkdir for imports

* fix: implement security scope check in rust recursive scanner

* refactor and format code

---------

Co-authored-by: Huang Xin <chrox.huang@gmail.com>
2026-01-20 06:22:04 +01:00
Huang Xin d9a6cffe78 feat(discord): support show reading status with Discord Rich Presence, closes #1538 (#2998) 2026-01-19 17:42:19 +01:00
Huang Xin 1704736bc8 feat(account): support social login with Discord account (#2995) 2026-01-19 05:22:17 +01:00
Huang Xin 6ce9a263ee fix(a11y): add missing aria labels for some buttons on TTS panel (#2994) 2026-01-19 04:29:02 +01:00
Huang Xin 038eca4267 fix(eink): resolved an issue where the dropdown menu would occasionally fail to expand on some Eink devices (#2991) 2026-01-18 19:57:09 +01:00
Huang Xin f0722ec0fe feat(bookshelf): show one line of book description in list view of the bookshelf, closes #2955 (#2990) 2026-01-18 17:16:54 +01:00
Huang Xin fd299a61a7 feat(notes): export notes with custom template, closes #1734 (#2989) 2026-01-18 16:56:32 +01:00
Huang Xin 36b7386e30 fix(portable): in-app update the portable version app, closes #2983 (#2988) 2026-01-18 11:20:15 +01:00
Huang Xin 2670b0dc0b fix(linux): use new AppImage format on Linux, closes #190 (#2985)
See https://github.com/tauri-apps/tauri/pull/12491
2026-01-18 09:50:51 +01:00
Huang Xin c44705e269 perf(pdf): pre-render next page for PDFs (#2984)
This should close #1911 and #13.
2026-01-18 08:58:00 +01:00
Huang Xin 7e618d456e chore: bump pdf.js to the latest version (#2982) 2026-01-17 13:45:34 +01:00
Huang Xin b28ac99a9e feat(pdf): support panning on PDFs (#2981)
This also closes #2978 and closes #2875.
2026-01-17 09:11:33 +01:00
Huang Xin 2d7d6b08a9 compat(opds): parse attachment filename from download requests, closes #2969 (#2976) 2026-01-16 17:13:30 +01:00
Huang Xin b1419f9c01 chore: bump tauri to latest dev (#2975) 2026-01-16 14:52:22 +01:00
Huang Xin 32ea42a835 fix: remove non-breaking space in book title (#2974) 2026-01-16 12:02:41 +01:00
Huang Xin 6083de3293 fix(import): read permissions of nested directories when importing books, closes #2954 (#2972) 2026-01-16 08:04:07 +01:00
Huang Xin 1c9cfa49b3 fix(flathub): use custom dbus id for single instance on Linux (#2971) 2026-01-16 05:59:25 +01:00
Huang Xin f85d6d4293 fix(eink): avoid scrolling if animation is turned off or in eink mode, closes #2957 (#2967) 2026-01-15 17:37:38 +01:00
Huang Xin 017d0b0b39 fix(tts): fix setting playback rate on Linux, closes #2950 (#2966) 2026-01-15 17:29:26 +01:00
Huang Xin 3146ae48a7 fix(proofread): support replace text with space (#2965) 2026-01-15 14:10:05 +01:00
Huang Xin 7f26e45ae7 feat(sync): cloud deletion in transfer queue (#2964) 2026-01-15 13:50:24 +01:00
Huang Xin 7d97826e4b feat(tts): replace words for TTS, closes #2057 (#2952) 2026-01-14 16:42:11 +01:00
Huang Xin 9c9c79176d feat(bookshelf): add an option to sort books by publish date, closes #2925 (#2949) 2026-01-14 13:40:07 +01:00
Huang Xin ed476a4fce fix(ios): fix wakelock on iOS, closes #2453 (#2948) 2026-01-14 13:06:16 +01:00
Huang Xin 44b4f7995b fix(android): fix annotator on Android, closes #2927 (#2946) 2026-01-14 11:56:38 +01:00
Huang Xin da5e3a0bd3 perf(fonts): cache first for font cache (#2945) 2026-01-14 07:34:00 +01:00
Huang Xin ba4678cc23 fix(fonts): fix CORS access error 403 of deno.dev with Origin: tauri://localhost (#2944) 2026-01-14 05:35:27 +01:00
Huang Xin e5eb3014b4 chore(unittest): test makeSafeFilename (#2943) 2026-01-14 04:08:30 +01:00
Huang Xin ed77b0bc7f fix(android): only dismiss unpinned sidebar and notebook with back key, closes #2920 (#2939) 2026-01-13 15:42:03 +01:00
Huang Xin 434a44e62c feat(proofread): add an option for whole word replacement, closes #2934 (#2938) 2026-01-13 15:35:57 +01:00
Huang Xin aaee04c290 fix(opds): probe filename when downloading PDFs and CBZ files, closes #2921 (#2932) 2026-01-12 17:55:50 +01:00
Huang Xin 48e2bfa82c fix(android): avoid occasional crash on start on Android (#2931) 2026-01-12 16:27:25 +01:00
Huang Xin c04f19ffb4 feat: add support for exporting book files in book details dialog, closes #2919 (#2930) 2026-01-12 16:26:32 +01:00
Huang Xin 4a624e3902 fix(settings): avoid stale viewSettings after saving, closes #2912 (#2916) 2026-01-11 16:47:36 +01:00
Huang Xin f53cee9616 release: version 0.9.97 (#2909) 2026-01-10 15:51:20 +01:00
Huang Xin 30385ee5ec fix(settings): correctly setting configs for selected book in parallel read, closes #2825 (#2908) 2026-01-10 15:01:04 +01:00
江夏尧 b30dfb3e23 fix(fonts): update font CDN links to use deno.dev for improved reliability (#2906) 2026-01-10 13:47:19 +01:00
Huang Xin c0d6102857 fix(css): primary btn style for e-ink mode (#2905) 2026-01-10 13:13:32 +01:00
Huang Xin 4537c55e84 fix(css): respect css for page break and minimum font size, closes #2895 (#2903) 2026-01-10 10:30:21 +01:00
Huang Xin aff94c0ab8 fix(windows): update shortcut to point to current installation, closes #2878 (#2902) 2026-01-10 10:26:32 +01:00
Huang Xin fd70836308 fix(windows): update shortcut to point to current installation, closes #2878 (#2901) 2026-01-10 09:53:40 +01:00
Huang Xin 1b0c94b9a5 fix(opds): temporary workaround for self-signed cert for OPDS server, closes #2871 (#2900)
Related to https://github.com/seanmonstar/reqwest/issues/1554
2026-01-10 08:47:12 +01:00
Huang Xin 7cb523eefc fix(metadata): no need to download book to display book details, closes #2857 (#2897) 2026-01-09 15:58:01 +01:00
Huang Xin 941be80cc4 fix(css): table and header layout, closes #2874 (#2896) 2026-01-09 15:06:37 +01:00
Huang Xin 5eecc735aa fix(proofread): support text replacement for text with no word boundaries, closes #2889 (#2894) 2026-01-09 10:27:38 +01:00
Huang Xin cfe51d01ee fix(sanitizer): normalize non-breaking spaces for WebView compatibility (#2893)
Convert &nbsp; to &#160; before sanitization and restore after XMLSerializer
serialization. This handles the platform difference where XMLSerializer
produces different representations of non-breaking spaces (&nbsp;, &#160;,
or \u00A0) across different WebView implementations.
2026-01-09 09:30:04 +01:00
Huang Xin 20ae09c52d perf(sync): persist partial library sync to prevent full retry on failure (#2892) 2026-01-09 09:23:02 +01:00
Huang Xin b868146129 feat(config): add an option to toggle footbar by tapping on the footbar (#2891) 2026-01-09 03:22:34 +01:00
Hermotimus a312080f7c fix(tts): footnotes anchors and superscript filtering (#2890) 2026-01-09 02:16:07 +01:00
Huang Xin 462ca46fee feat(eink): optimize color and layout for e-ink mode (#2887) 2026-01-08 17:29:33 +01:00
Huang Xin 93228c4b2a fix: avoid hydration mismatch for tauri app (#2884) 2026-01-08 04:05:10 +01:00
Huang Xin 2ff10f781f feat(annotator): support vertical layout for annotation editor (#2882) 2026-01-07 16:18:57 +01:00
Huang Xin 9614c62360 feat(annotator): instant highlighting with mouse, touch or pen (#2881) 2026-01-07 16:13:37 +01:00
lcd1232 5ffaac5e67 fix(login): fix login message field (#2879) 2026-01-07 12:55:11 +01:00
Huang Xin 71af91608f feat(annotator): support editing text range of highlights (#2870) 2026-01-05 16:25:58 +01:00
Huang Xin 9603b61776 refactor(annotatot): more cleaner text selector on Android (#2867) 2026-01-05 08:53:38 +01:00
Huang Xin eed84a059a fix(search): should be able to terminate search when sidebar is invisible (#2863) 2026-01-04 17:10:49 +01:00
Huang Xin 604ef65719 perf(search): use cache for search results (#2861) 2026-01-04 12:56:40 +01:00
Huang Xin 483d536ca4 feat(search): support search terms history, closes #2836 (#2859) 2026-01-04 07:47:43 +01:00
Huang Xin 69a51c5880 compat(footnote): support alt footnote inside placeholder anchor (#2858) 2026-01-04 06:06:24 +01:00
Huang Xin ca8d25341e compat(opds): support Komga OPDS v1.2 and v2, closes #2839 (#2851) 2026-01-03 18:04:39 +01:00
Huang Xin 8a263235ed feat(search): add annotations navigation bar, closes #2060 (#2849) 2026-01-03 14:57:10 +01:00
Huang Xin fb41ff5d0c fix(tts): reduce the pause duration between sentences in Edge TTS, closes #2837 (#2844) 2026-01-03 08:44:15 +01:00
Huang Xin a5e09e8454 feat(search): add search results navigation bar, closes #1183 (#2842)
Add a floating navigation component that appears when search results exist.
The component includes:
   - Top bar: displays search term and current section with TOC and close buttons
   - Bottom bar: search results, previous, and next navigation buttons
   - Page-based navigation using isCfiInLocation to skip between pages with results
2026-01-03 08:10:23 +01:00
Huang Xin 730ee21651 fix(android): don't navigate back with the back button for more predictable navigation (#2835)
This will revert the link navigation with back button in #2454.
This will close #2833 and more user reports that found back link navigation confusing.
2026-01-02 15:44:47 +01:00
Huang Xin dea43445c3 fix(layout): set image width for vertical inline images (#2832) 2026-01-02 14:54:02 +01:00
Huang Xin 4f0ae78d43 fix(ios): fixed error when importing file with urlencoded names (#2831) 2026-01-02 14:35:11 +01:00
Huang Xin d9116d619a feat(css): support overriding link color (#2830) 2026-01-02 14:15:02 +01:00
Huang Xin c080e6fdd3 fix(metadata): fixed sometimes svg book cover is rendered blank (#2829) 2026-01-02 14:13:24 +01:00
Huang Xin d3752dadc6 fix(library): upload files also for open with import and opds import, closes #2826 (#2827) 2026-01-02 13:32:52 +01:00
André Angelantoni e21ef53a3d fix(settings): ensure global settings sync across all open panes (#2818)
* fix(settings): ensure global settings sync across all open panes

Previously, changing a global setting (like font size) only updated the currently active book pane, causing split-view panes to desynchronize.

This commit updates the settings logic to propagate global changes to all open book instances immediately. It also ensures that settings UI panels correctly re-render when preferences are updated from outside the component.

* refactor to reuse some code

* fix: pointer in doc check

---------

Co-authored-by: André Angelantoni <andre.angelantoni@performantlabs.com>
Co-authored-by: Huang Xin <chrox.huang@gmail.com>
2026-01-01 07:12:07 +01:00
Huang Xin 71e97998b6 feat(transfer): add a background transfer queue system for book uploads/downloads (#2821) 2025-12-31 08:40:28 +01:00
Huang Xin 22f9c45232 fix(annotator): delay highlight action to prevent immediate onShowAnnotation trigger (#2820) 2025-12-31 08:35:26 +01:00
Huang Xin a42897ec5c feat(annotator): support quick actions for text selection, closes #2505 (#2813) 2025-12-30 10:32:01 +01:00
Huang Xin 58a5c1625c fix(css): only override background color of hr when it has background image (#2808) 2025-12-30 10:24:37 +01:00
Huang Xin d53fc09e1e chore: bump opennext, wrangler and serwist to the latest versions (#2807) 2025-12-29 05:12:32 +01:00
Huang Xin 11fecb5dc0 chore: bump tauri to latest dev (#2805) 2025-12-29 04:07:58 +01:00
Huang Xin f4587663b5 chore(ios): drop support for iOS below 15.0 (#2804) 2025-12-29 03:41:12 +01:00
Huang Xin b76a2adf61 compat(epub): detect missing writing direction for some EPUBs (#2803) 2025-12-28 19:43:49 +01:00
Huang Xin 988fbc8c85 fix(settings): override book text align of left (#2802) 2025-12-28 18:02:56 +01:00
Huang Xin f944ad9b9f feat(annotator): support rendering vertical annotation bubbles (#2801) 2025-12-28 13:52:12 +01:00
Huang Xin 335d91b9c9 fix(layout): dismiss annotation popup on mobile when navigating (#2799) 2025-12-28 07:18:03 +01:00
Huang Xin 173404eaad feat(annotator): show notes popup when annotation bubble icon is clicked, closes #1845 (#2798) 2025-12-27 17:31:47 +01:00
Huang Xin 674fed5230 fix(annotator): adjust underline position to the center of two lines, closes #2772 (#2793) 2025-12-26 09:51:02 +01:00
Huang Xin 12d284cd22 fix(layout): fixed line clamp for config items on small screens (#2792) 2025-12-26 05:05:37 +01:00
Huang Xin 77037c8adb fix(sw): use NetworkFirst for navigation to prevent blank page on updates (#2784) 2025-12-24 15:19:14 +01:00
Huang Xin 6984393ed1 refactor(proofread): refactor UI and i18n for proofread tool (#2783) 2025-12-24 14:46:33 +01:00
Huang Xin 3abef6ea75 fix(opds): correctly parse file extension for CBZ files from OPDS servers, closes #2765 (#2771) 2025-12-23 08:53:50 +01:00
Huang Xin 4ae1ab7dd0 fix(layout): workaround for hardcoded image container (#2769) 2025-12-23 06:31:09 +01:00
Huang Xin 69fb22119b fix(layout): tweak insets for vertical layout (#2767) 2025-12-23 06:01:06 +01:00
Huang Xin 3a6c18c6d5 feat(font): support OpenType feature of vrt2 and vert for vertical replacement of some punctuations (#2764) 2025-12-22 18:39:09 +01:00
Huang Xin 7db1bc460d chore(pwa): migrate from next-pwa to serwist (#2762) 2025-12-22 16:10:09 +01:00
Huang Xin a460e609fa refactor(file): avoid eviction race of chunks cache (#2761) 2025-12-22 05:59:40 +01:00
Huang Xin 4b4ebdbdaa fix(pdf): avoid frequent eviction of chunk cache from worker thread, closes #2757 (#2759) 2025-12-21 17:47:41 +01:00
Huang Xin 9358a06839 compat(layout): grid layout with fit covers on older browsers, closes #2745 (#2756) 2025-12-21 08:52:57 +01:00
Huang Xin a7baf6cc9f fix(tts): prompt users to log in to use Edge TTS on the web version (#2749) 2025-12-19 17:54:17 +01:00
Huang Xin 15dc669f35 chore: update app metainfo (#2744) 2025-12-19 04:54:24 +01:00
Huang Xin c853957512 release: version 0.9.96 (#2743) 2025-12-19 03:54:17 +01:00
Huang Xin 8a4e22e423 refactor: temporarily disable the proofreading feature for a hotfix release ahead of a major refactor (#2742) 2025-12-19 03:45:42 +01:00
Huang Xin 8a43c58fd4 fix(tts): resolve Edge TTS being blocked in certain regions (#2741)
This should close #2739 and close #1821.
2025-12-19 03:24:51 +01:00
Qianxue Ge 54fdf5f1fd feat(replacement): text replacement feature for EPUB books (#2725)
* add: basic ui replacement menu

* feat(replacement): modified ViewSettings interface and added Replacement type

* add: frontend menu ui to annotation settings
- create replacementoptions file for 4 fix options: fix once, fix in library, fix in book, fix in library
-integrate with annotator.tsx
only frontend changes, but initialzied in backend

* add: delete global option and click gear option to get rid of menu

* docs: add test cases for replacementoptions file

* edits to enable readest to build

* basic changes for rule types

* replacement transformer file added

* additional support code added

* interim updates to replacement.ts file

* adding console log statements to confirm functionality without frontend

* adding more console logs for debugging; i think i got my replacement working, will clean console logs and add actual tests now.

* figured out how to get my transformer to work. replacement doesnt actually work yet. figuring that out rn. committing before i destroy something, lol

* replcement logic working with hard coded tests. code is cleaned up with minimal console logs. actual replacement logic + testing is next :)

* test suite built, and fully passing. made consle log edits too.

* added more replacement rules, but figuring out why they arent being implemented by my code.

* cleaning up test suite to not break when there are 0 rules; test is commited with 1 local rule. not sure if that rule is going to copy over when i merge.

* feat(replacement): Add text field, case sensitivity checkbox, and confirmation dialog to ReplacementOptions

- Add text input field for replacement text with placeholder
- Add 'Case Sensitive' checkbox (default: unchecked/case-insensitive)
- Implement two-step confirmation flow with Back/Confirm buttons
- Show preview of original text, replacement text, scope, and case sensitivity
- Disable scope buttons until replacement text is entered
- Display truncated preview for long selected text (>50 chars)
- Export ReplacementConfig type for use in parent components

* feat(replacement): Add 30-word limit and integrate new ReplacementOptions component

- Add MAX_REPLACEMENT_WORDS constant (30 words)
- Add getWordCount() utility function for word counting
- Show warning toast when word limit exceeded on Text Replacement click
- Replace old fix handlers with single handleReplacementConfirm()
- Integrate with new ReplacementConfig (replacementText, caseSensitive, scope)
- Display success toast with scope and case sensitivity info on confirm

* fix(build): Add ReplacementMenu placeholder component

- Create placeholder component to fix missing import error in reader/page.tsx
- Component returns null for now, to be implemented with global replacement rules

* test(replacement): Add comprehensive tests for ReplacementOptions and word limit

ReplacementOptions.test.tsx:
- Test rendering of text input, checkbox, and scope buttons
- Test case sensitivity checkbox toggle and state
- Test disabled buttons when no replacement text entered
- Test confirmation dialog flow and Back/Confirm buttons
- Test click outside and Cancel button behavior
- Test full replacement flow with all options

wordLimit.test.ts:
- Test word counting with various inputs (spaces, newlines, unicode)
- Test 30-word limit boundary conditions
- Test case-sensitive vs case-insensitive matching logic
- Test edge cases (empty string, long words, punctuation)

* refactor: removed unused initial definition of Replacement

* feat: added replacement rules window in bookmenu

* test: added tests to verify the replacement rules window renders book and global replacement rules, and it opens when bookmenu item is clicked

* feat: added Replacement tab in SettingsDialog, displays global rules

* feat(replacement): connected front-end to functions. todo: fix the automatic reload functionality.

* fix(replacement): simplified re-rendering logic, doesn't fail on epubs anymore.

* test: add integration tests for text replacement functionality

* fix: added single rules section to ReplacementRulesWindow

* fix(replacement): added null checks to some unsafe calls in integration tests

* fix(replacements): added non-null assertion operator for a previously initialized variable

* refactor: created ReplacementPanel and edited style of inputs

* feat: disable the edit feature for selected phrase

* refactor: use toast instead of banner for confirmation msg

* feat: automatically reload the page to apply changes

* feat: disable global rule for book if deleted in book view

* fix(replacement): Improve popup positioning and eliminate ghost animation

- Add viewport boundary detection to keep popup within visible area
- Calculate position only once on mount to prevent jumping when other UI appears
- Use visibility: hidden until position is calculated to eliminate ghost animation
- Add max-height with overflow-y: auto for scrollable content
- Popup now appears directly in correct position without two-step animation

* fix: implement single-instance replacement with persistence

- Add sectionHref to TransformContext for section tracking
- Add singleInstance, sectionHref, occurrenceIndex fields to ReplacementRule
- Pass section name from FoliateViewer to transformer context
- Switch transformer from DOM-based to string-based replacement
- Handle single-instance rules with section matching and occurrence tracking
- Update Annotator to track occurrence index and apply direct DOM changes
- Persist single-instance rules for refresh survival

Single-instance replacements now:
1. Apply immediately via direct DOM modification
2. Store occurrence index and section for precise targeting
3. Persist across page refreshes

* fix: allow multiple single-instance replacements for same word

Single-instance rules now always create new entries instead of merging.
This fixes the issue where replacing multiple occurrences of the same
word would overwrite previous rules.

The transformer applies rules in sequence, so each rule targets
occurrence index 0 of the current (modified) string, allowing
cascading replacements to work correctly after refresh.

* fix: prevent cascading replacements and add wholeWord support

- Add wholeWord field to ReplacementRule for word boundary matching
- Track replaced regions to prevent replacement text from being re-matched
- Fix cascading replacement issue where replacement text was matched again
- Apply replacements from right to left to preserve positions
- Support whole word matching with \b boundaries for both single-instance and regular rules

* Fix whole-word matching for replacement rules

- Auto-enforce whole-word matching for simple word patterns (letters only)
- Add HTML tag boundary checks to prevent matching across tags
- Add double-check validation for whole-word matches
- Prevent matching 'and' inside words like 'England', 'stand', 'understand'
- Add comprehensive logging for debugging replacement issues

* test: added rAF in setup to for ReplacementOptions tests

* fix: only allow replacement for epubs, remove replacement rendering for non-epubs, add test cases

* refactor: refactored replacement logic for case sensitivity and word boundaries

* test: added tests for scope precedence and case sensitivity across scopes

* refactor: removed unnecessary code from testing

* feat: able to display, edit, and delete single-instance rules in book settings

* fix: connected case sensitive checkbox to backend, fixed merge and delete logic

* test: updated test cases to reflect changes on case sensitivity and rules rendering

* test: modified ReplacementOptions test to remove unnecessary case sensitive check from merge

* fix: add logic for grayed out button for non-epubs

* chore: update foliate-js submodule from upstream merge

* fix: resolve all TypeScript/ESLint linting errors

- Fix prefer-const error in ReplacementOptions.tsx
- Fix set-state-in-effect error in ReplacementRulesWindow.tsx (use lazy initializer)
- Replace all @typescript-eslint/no-explicit-any with proper types (ReplacementRule, unknown, etc.)
- Fix unused error variables in replacement.ts (prefix with _)
- Remove unused eslint-disable directives
- Add missing ReplacementRule import in ReplacementPanel.tsx

* fix: add localStorage mock to vitest setup

- Fixes test failures in ReplacementRulesWindow and SettingsDialog tests
- localStorage mock ensures all Storage API methods are available in test environment

* fix: resolve ESLint and TypeScript build errors

- Fix all remaining @typescript-eslint/no-explicit-any errors in test files
- Fix unused error variables in replacement.ts (prefix with _)
- Fix TypeScript error in ReplacementRulesWindow.tsx (move @ts-ignore to correct location)
- All ESLint checks now pass
- Web and Tauri builds compile successfully

* fix: remove lookbehind regex for browser compatibility

- Replace lookbehind assertions (?<!...) with manual boundary checking
- Add isUnicodeWordChar helper function for manual Unicode word boundary detection
- Apply manual boundary checks in applyMultiReplacement and applySingleInstance
- Fixes build_web_app check failures by avoiding lookbehind in compiled output
- Maintains whole-word matching functionality for both ASCII and Unicode patterns

* fix: update tauri-utils version to 2.8.1 to resolve duplicate symbol error

- Update local tauri-utils version from 2.8.0 to 2.8.1 to match crates.io version
- Fixes duplicate symbol __TAURI_BUNDLE_TYPE linker error
- Ensures all dependencies use the same tauri-utils version

* fix: use local tauri path directly to resolve version conflicts

- Change tauri dependency to use local path instead of version requirement
- This ensures all dependencies use the same local tauri version (2.9.3)
- Fixes 'links = Tauri' conflict error in Rust linting
- The patch.crates-io should still work for transitive dependencies

* fix: use version requirement with patch for tauri dependency

- Revert to using version requirement '2' instead of direct path
- Rely on [patch.crates-io] to use local tauri version
- Remove Cargo.lock to force fresh dependency resolution
- This should resolve the 'links = Tauri' conflict by ensuring
  all tauri dependencies (direct and transitive) use the patched version

* fix: remove plugin patches that cause resolution errors

- Remove all tauri-plugin git patches from [patch.crates-io]
- Keep only tauri, tauri-utils, and tauri-build patches
- Plugins from crates.io will use the patched tauri via transitive dependencies
- Fixes error: patch for tauri-plugin-oauth failed to resolve

* fix: update tauri submodule with tauri-utils version fixes

* fix: revert tauri submodule and update tauri-utils to 2.8.0

- Revert submodule changes that can't be pushed to remote
- Update local tauri-utils version to 2.8.0 to match other packages
- This avoids the need to modify the submodule

* fix: add tauri-plugin to workspace and patch to resolve duplicate symbol error

- Add packages/tauri/crates/tauri-plugin to workspace members
- Add tauri-plugin patch to [patch.crates-io]
- This ensures all tauri dependencies use local versions
- Fixes duplicate symbol __TAURI_BUNDLE_TYPE linking error

* chore: restore Cargo.lock from upstream

- Restore the original Cargo.lock from readest/readest main branch
- This ensures reproducible builds and matches upstream
- The lock file will be updated by cargo when dependencies change

* fix: resolve TypeScript errors in test files

- Fix ReplacementOptions.test.tsx: add optional chaining for possibly undefined values
- Fix ReplacementRulesWindow.test.tsx: use proper type assertions for store setState calls
- Use (store.setState as unknown as (state: unknown) => void) pattern for partial state updates

* fix: prevent race condition when deleting replacement rules rapidly

- Add isReloading state to track ongoing delete/edit operations
- Prevent multiple rapid deletions that cause runtime errors during page reload
- Show warning toast when user tries to delete while reload is in progress
- Add finally blocks to ensure isReloading is always reset
- This prevents the 'book doesn't finish rerendering' error

* fix: allow phrases and lines with quotes for single-instance replacements

- Updated isWholeWord() to allow phrases (text with spaces or punctuation)
- Phrases are always allowed for single-instance replacements
- Only single words are checked for partial word matches
- Fixes issue where lines with quotes couldn't be replaced
- Added detailed logging for debugging phrase detection

* fix: allow selections with boundary punctuation and fix pattern matching for punctuation

- Updated isWholeWord() to explicitly allow selections that start or end with punctuation (e.g., 'tis, off;, look,)
- Fixed normalizePattern() to handle patterns with leading/trailing punctuation correctly
- Word boundaries are now only added around the word part, not the punctuation
- Fixes issue where replacements like 'scholar;' were not matching correctly

* fix: escape HTML entities in replacement text to preserve angle brackets

- Added escapeHtmlEntities() function to escape HTML special characters
- Apply HTML escaping to replacement text in both multi and single-instance replacements
- Fixes issue where replacement text like '<<AND>>' was being interpreted as HTML tags
- Angle brackets and other HTML entities are now properly escaped and displayed correctly

* fix: revert Tauri backend changes and resolve package.json conflict

- Revert Cargo.toml and src-tauri/Cargo.toml to match upstream/main
- Resolve @tauri-apps/cli version conflict (2.9.5 -> 2.9.6)
- These changes are not related to the replacement feature implementation

* fix: update pnpm-lock.yaml to match @tauri-apps/cli 2.9.6

* removed useless tests and backend tests from ReplacementOptions integration testing suite

* chore: revert foliate-js submodule to match readest/readest main

* fix: refactored wordLimit logic into a separate util file

* fix: removed additional pr description

* refactor: rewrite replacement transformer to use DOM-based approach
replace string manipulation with DOMParser and TreeWalker
follow pattern from simpleecc transformer

* style: format code with prettier

* fix: remove unused string-manipulation functions

* fix: refactored display dialog logic to match other dialogs

* fix: enabled global rule deletion in book menu

* fix: removed ReplacementPanel from library settings

* fix: deleted SettingsDialog.replacement.test.tsx since we no longer need to display replacements in library settings

* fix: removed text replacement tab from settings dialog

* fix: applied prettier code formatter to replacement rules window

* chore: fix formatting and remove unused file listed by chrox

* chore: format all changed files from pr 2693 and revert pnpm-lock

* rebased Cargo.lock, package.json, pnpm-lock.yaml to upstream main
edits to enable readest to build

* basic changes for rule types

* replacement transformer file added

* additional support code added

* interim updates to replacement.ts file

* adding console log statements to confirm functionality without frontend

* adding more console logs for debugging; i think i got my replacement working, will clean console logs and add actual tests now.

* figured out how to get my transformer to work. replacement doesnt actually work yet. figuring that out rn. committing before i destroy something, lol

* replcement logic working with hard coded tests. code is cleaned up with minimal console logs. actual replacement logic + testing is next :)

* test suite built, and fully passing. made consle log edits too.

* added more replacement rules, but figuring out why they arent being implemented by my code.

* cleaning up test suite to not break when there are 0 rules; test is commited with 1 local rule. not sure if that rule is going to copy over when i merge.

* add: basic ui replacement menu

* add: frontend menu ui to annotation settings
- create replacementoptions file for 4 fix options: fix once, fix in library, fix in book, fix in library
-integrate with annotator.tsx
only frontend changes, but initialzied in backend

* add: delete global option and click gear option to get rid of menu

* docs: add test cases for replacementoptions file

* feat(replacement): modified ViewSettings interface and added Replacement type

feat(replacement): modified viewsettings interface and added ReplacementRulesConfig

* feat(replacement): Add text field, case sensitivity checkbox, and confirmation dialog to ReplacementOptions

- Add text input field for replacement text with placeholder
- Add 'Case Sensitive' checkbox (default: unchecked/case-insensitive)
- Implement two-step confirmation flow with Back/Confirm buttons
- Show preview of original text, replacement text, scope, and case sensitivity
- Disable scope buttons until replacement text is entered
- Display truncated preview for long selected text (>50 chars)
- Export ReplacementConfig type for use in parent components

* feat(replacement): Add 30-word limit and integrate new ReplacementOptions component

- Add MAX_REPLACEMENT_WORDS constant (30 words)
- Add getWordCount() utility function for word counting
- Show warning toast when word limit exceeded on Text Replacement click
- Replace old fix handlers with single handleReplacementConfirm()
- Integrate with new ReplacementConfig (replacementText, caseSensitive, scope)
- Display success toast with scope and case sensitivity info on confirm

* fix(build): Add ReplacementMenu placeholder component

- Create placeholder component to fix missing import error in reader/page.tsx
- Component returns null for now, to be implemented with global replacement rules

* test(replacement): Add comprehensive tests for ReplacementOptions and word limit

ReplacementOptions.test.tsx:
- Test rendering of text input, checkbox, and scope buttons
- Test case sensitivity checkbox toggle and state
- Test disabled buttons when no replacement text entered
- Test confirmation dialog flow and Back/Confirm buttons
- Test click outside and Cancel button behavior
- Test full replacement flow with all options

wordLimit.test.ts:
- Test word counting with various inputs (spaces, newlines, unicode)
- Test 30-word limit boundary conditions
- Test case-sensitive vs case-insensitive matching logic
- Test edge cases (empty string, long words, punctuation)

* refactor: removed unused initial definition of Replacement

* feat: added replacement rules window in bookmenu

* test: added tests to verify the replacement rules window renders book and global replacement rules, and it opens when bookmenu item is clicked

* feat: added Replacement tab in SettingsDialog, displays global rules

* fix: added single rules section to ReplacementRulesWindow

* refactor: created ReplacementPanel and edited style of inputs

* feat(replacement): connected front-end to functions. todo: fix the automatic reload functionality.

* fix(replacement): simplified re-rendering logic, doesn't fail on epubs anymore.

* test: add integration tests for text replacement functionality

* fix(replacement): added null checks to some unsafe calls in integration tests

* fix(replacements): added non-null assertion operator for a previously initialized variable

* feat: disable the edit feature for selected phrase

* refactor: use toast instead of banner for confirmation msg

* feat: automatically reload the page to apply changes

* feat: disable global rule for book if deleted in book view

* fix(replacement): Improve popup positioning and eliminate ghost animation

- Add viewport boundary detection to keep popup within visible area
- Calculate position only once on mount to prevent jumping when other UI appears
- Use visibility: hidden until position is calculated to eliminate ghost animation
- Add max-height with overflow-y: auto for scrollable content
- Popup now appears directly in correct position without two-step animation

* fix: only allow replacement for epubs, remove replacement rendering for non-epubs, add test cases

* fix: add logic for grayed out button for non-epubs

* fix: resolve all TypeScript/ESLint linting errors

- Fix prefer-const error in ReplacementOptions.tsx
- Fix set-state-in-effect error in ReplacementRulesWindow.tsx (use lazy initializer)
- Replace all @typescript-eslint/no-explicit-any with proper types (ReplacementRule, unknown, etc.)
- Fix unused error variables in replacement.ts (prefix with _)
- Remove unused eslint-disable directives
- Add missing ReplacementRule import in ReplacementPanel.tsx

* fix: add localStorage mock to vitest setup

- Fixes test failures in ReplacementRulesWindow and SettingsDialog tests
- localStorage mock ensures all Storage API methods are available in test environment

* fix: implement single-instance replacement with persistence

- Add sectionHref to TransformContext for section tracking
- Add singleInstance, sectionHref, occurrenceIndex fields to ReplacementRule
- Pass section name from FoliateViewer to transformer context
- Switch transformer from DOM-based to string-based replacement
- Handle single-instance rules with section matching and occurrence tracking
- Update Annotator to track occurrence index and apply direct DOM changes
- Persist single-instance rules for refresh survival

Single-instance replacements now:
1. Apply immediately via direct DOM modification
2. Store occurrence index and section for precise targeting
3. Persist across page refreshes

* fix: allow multiple single-instance replacements for same word

Single-instance rules now always create new entries instead of merging.
This fixes the issue where replacing multiple occurrences of the same
word would overwrite previous rules.

The transformer applies rules in sequence, so each rule targets
occurrence index 0 of the current (modified) string, allowing
cascading replacements to work correctly after refresh.

* fix: prevent cascading replacements and add wholeWord support

- Add wholeWord field to ReplacementRule for word boundary matching
- Track replaced regions to prevent replacement text from being re-matched
- Fix cascading replacement issue where replacement text was matched again
- Apply replacements from right to left to preserve positions
- Support whole word matching with \b boundaries for both single-instance and regular rules

* Fix whole-word matching for replacement rules

- Auto-enforce whole-word matching for simple word patterns (letters only)
- Add HTML tag boundary checks to prevent matching across tags
- Add double-check validation for whole-word matches
- Prevent matching 'and' inside words like 'England', 'stand', 'understand'
- Add comprehensive logging for debugging replacement issues

* refactor: refactored replacement logic for case sensitivity and word boundaries

* test: added tests for scope precedence and case sensitivity across scopes

* refactor: removed unnecessary code from testing

* feat: able to display, edit, and delete single-instance rules in book settings

* fix: connected case sensitive checkbox to backend, fixed merge and delete logic

* test: updated test cases to reflect changes on case sensitivity and rules rendering

* test: modified ReplacementOptions test to remove unnecessary case sensitive check from merge

* fix: resolve ESLint and TypeScript build errors

- Fix all remaining @typescript-eslint/no-explicit-any errors in test files
- Fix unused error variables in replacement.ts (prefix with _)
- Fix TypeScript error in ReplacementRulesWindow.tsx (move @ts-ignore to correct location)
- All ESLint checks now pass
- Web and Tauri builds compile successfully

* fix: update tauri-utils version to 2.8.1 to resolve duplicate symbol error

- Update local tauri-utils version from 2.8.0 to 2.8.1 to match crates.io version
- Fixes duplicate symbol __TAURI_BUNDLE_TYPE linker error
- Ensures all dependencies use the same tauri-utils version

* fix: use local tauri path directly to resolve version conflicts

- Change tauri dependency to use local path instead of version requirement
- This ensures all dependencies use the same local tauri version (2.9.3)
- Fixes 'links = Tauri' conflict error in Rust linting
- The patch.crates-io should still work for transitive dependencies

* fix: use version requirement with patch for tauri dependency

- Revert to using version requirement '2' instead of direct path
- Rely on [patch.crates-io] to use local tauri version
- Remove Cargo.lock to force fresh dependency resolution
- This should resolve the 'links = Tauri' conflict by ensuring
  all tauri dependencies (direct and transitive) use the patched version

* fix: remove plugin patches that cause resolution errors

- Remove all tauri-plugin git patches from [patch.crates-io]
- Keep only tauri, tauri-utils, and tauri-build patches
- Plugins from crates.io will use the patched tauri via transitive dependencies
- Fixes error: patch for tauri-plugin-oauth failed to resolve

* fix: add tauri-plugin to workspace and patch to resolve duplicate symbol error

- Add packages/tauri/crates/tauri-plugin to workspace members
- Add tauri-plugin patch to [patch.crates-io]
- This ensures all tauri dependencies use local versions
- Fixes duplicate symbol __TAURI_BUNDLE_TYPE linking error

* chore: restore Cargo.lock from upstream

- Restore the original Cargo.lock from readest/readest main branch
- This ensures reproducible builds and matches upstream
- The lock file will be updated by cargo when dependencies change

* fix: resolve TypeScript errors in test files

- Fix ReplacementOptions.test.tsx: add optional chaining for possibly undefined values
- Fix ReplacementRulesWindow.test.tsx: use proper type assertions for store setState calls
- Use (store.setState as unknown as (state: unknown) => void) pattern for partial state updates

* fix: allow selections with boundary punctuation and fix pattern matching for punctuation

- Updated isWholeWord() to explicitly allow selections that start or end with punctuation (e.g., 'tis, off;, look,)
- Fixed normalizePattern() to handle patterns with leading/trailing punctuation correctly
- Word boundaries are now only added around the word part, not the punctuation
- Fixes issue where replacements like 'scholar;' were not matching correctly

* fix: prevent race condition when deleting replacement rules rapidly

- Add isReloading state to track ongoing delete/edit operations
- Prevent multiple rapid deletions that cause runtime errors during page reload
- Show warning toast when user tries to delete while reload is in progress
- Add finally blocks to ensure isReloading is always reset
- This prevents the 'book doesn't finish rerendering' error

* fix: escape HTML entities in replacement text to preserve angle brackets

- Added escapeHtmlEntities() function to escape HTML special characters
- Apply HTML escaping to replacement text in both multi and single-instance replacements
- Fixes issue where replacement text like '<<AND>>' was being interpreted as HTML tags
- Angle brackets and other HTML entities are now properly escaped and displayed correctly

* fix: revert Tauri backend changes and resolve package.json conflict

- Revert Cargo.toml and src-tauri/Cargo.toml to match upstream/main
- Resolve @tauri-apps/cli version conflict (2.9.5 -> 2.9.6)
- These changes are not related to the replacement feature implementation

* fix: update pnpm-lock.yaml to match @tauri-apps/cli 2.9.6

* removed useless tests and backend tests from ReplacementOptions integration testing suite

* chore: revert foliate-js submodule to match readest/readest main

* fix: refactored display dialog logic to match other dialogs

* fix: enabled global rule deletion in book menu

* fix: removed ReplacementPanel from library settings

* fix: deleted SettingsDialog.replacement.test.tsx since we no longer need to display replacements in library settings

* fix: removed text replacement tab from settings dialog

* fix: applied prettier code formatter to replacement rules window

* fix: refactored wordLimit logic into a separate util file

* fix: removed additional pr description

* style: format code with prettier

* chore: fix formatting and remove unused file listed by chrox

* chore: format all changed files from pr 2693 and revert pnpm-lock

* fix: fixed inconsistencies from rebase

* refactor: removed unused code

* refactor: removed unintentional formatting changes

* fix: set upstream for packages/tauri-plugins to the readest branch

* fix: used original Cargo.lock file

* fix: got Cargo.lock from upstream

* fix: fetched SettingsDialog from upstream main

* fix: pointed tauri-plugins to the same commit as upstream

* chore: remove unnecssary comments from replacement.ts

* chore: fixed more unnecessary comments

---------

Co-authored-by: fatbiscuit247 <fatbiscuit247@github.com>
Co-authored-by: joon <your.email@example.com>
Co-authored-by: jarchenn <jerryc2@andrew.cmu.edu>
Co-authored-by: joon0429 <68578999+joon0429@users.noreply.github.com>
Co-authored-by: Jerry Chen <50bmg@Jerrys-MacBook-Pro-9.local>
Co-authored-by: Alicia Chen <aliciach@andrew.cmu.edu>
Co-authored-by: Jerry Chen <50bmg@MacBook-Pro-7.local>
Co-authored-by: Jerry Chen <50bmg@macbook-pro-158.wifi.local.cmu.edu>
Co-authored-by: fatbiscuit247 <136537548+fatbiscuit247@users.noreply.github.com>
2025-12-17 10:06:59 +08:00
Huang Xin fe50b513b3 fix(layout): line clamp opds url, closes #2726 (#2731) 2025-12-16 17:03:15 +01:00
Huang Xin 17c7fa8f41 fix: make sidebar and notebook pin states persist after refresh (#2730) 2025-12-16 16:16:21 +01:00
Huang Xin 2533560d11 fix(layout): fix bleed layout for images (#2729) 2025-12-16 15:46:13 +01:00
Huang Xin 5850a16afd fix: add stats API and fix fd leak, closes #2323 (#2723) 2025-12-16 06:51:48 +01:00
Huang Xin 7063d62b13 fix(settings): screen brightness setting only applies to the reader page, closes #2717 (#2720) 2025-12-15 06:04:29 +01:00
dependabot[bot] 0bd6a217ae chore(deps): bump actions/cache from 4 to 5 in the github-actions group (#2719)
Bumps the github-actions group with 1 update: [actions/cache](https://github.com/actions/cache).


Updates `actions/cache` from 4 to 5
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-15 05:14:57 +01:00
Huang Xin b7df294d78 feat(bookshelf): add group books button in context menu, closes #2698 (#2718) 2025-12-15 05:02:52 +01:00
Huang Xin 5aa78f2554 fix(opds): expose X-Content-Length header for CORS requests (#2715) 2025-12-14 19:06:09 +01:00
Huang Xin e740571c33 feat(opds): instant search bar for opds catalog, closes #2707 (#2714) 2025-12-14 18:25:47 +01:00
Huang Xin e6d9913f4e fix(layout): make sure annotation popups can be accessible in some edge cases, closes #2704 (#2713) 2025-12-14 05:52:55 +01:00
Huang Xin 1869a863a3 fix(layout): fixed max inline width not applied for EPUBs, closes #2706 (#2711) 2025-12-13 18:52:26 +01:00
Huang Xin 524de92f5e feat(macOS): add open file global menu for macOS, closes #2692 (#2708) 2025-12-13 17:36:25 +01:00
Huang Xin c1530cc5c4 feat(iOS): support open file with Readest in Files App, closes #2334 (#2705) 2025-12-13 13:28:03 +01:00
Huang Xin 730fadb834 fix(web): fixed router glitches for library page after returned from reader (#2703) 2025-12-13 08:08:42 +01:00
Huang Xin 383e5c61b1 chore: bump next.js to version 16.0.10 (#2702) 2025-12-13 07:54:15 +01:00
Huang Xin 6d42086fa7 fix(layout): fixed the layout of the selector of the translator providers (#2701) 2025-12-13 05:14:12 +01:00
mikepmiller 5a20fae204 feat(ui): progress info with cycleable display modes (#2682)
* Hideable Progress View
* feat: cycle between progress info modes

---------

Co-authored-by: Huang Xin <chrox.huang@gmail.com>
2025-12-12 08:19:07 +01:00
Huang Xin 34fd64c5c4 fix(sync): handle special characters in filenames when downloading (#2694) 2025-12-11 19:20:27 +01:00
Huang Xin 0874fb0764 chore: bump tauri to the latest dev branch (#2690) 2025-12-11 13:46:41 +01:00
Huang Xin e03ed5b604 fix(layout): fix responsive layout for footnote popup (#2688) 2025-12-11 10:15:56 +01:00
Huang Xin 41edc89ac7 fix(pwa): don't cache api requests and cache client-side navigation routes (#2687) 2025-12-11 07:30:19 +01:00
Huang Xin f0a470398d chore(pwa): more aggressive offline cache for the web version (#2686) 2025-12-11 05:24:45 +01:00
Huang Xin 51008d81fb fix(opds): select proper opds search link (#2683) 2025-12-10 19:56:27 +01:00
Huang Xin 9828904674 feat(opds): added books catalog from standardebooks.org (#2681) 2025-12-10 18:20:56 +01:00
Huang Xin 2670d835b3 fix(comic): fixed layout for comic books, closes #2672 (#2680) 2025-12-10 18:08:11 +01:00
Huang Xin 8b7bafc4b6 chore(koplugin): add version info in the meta file (#2679) 2025-12-10 16:52:28 +01:00
Huang Xin ca759e0246 fix(tts): avoid false default en language code for TTS (#2678) 2025-12-10 16:24:01 +01:00
Huang Xin 5141be1c3f fix(iap): don't initialize billing on Android without google play service, closes #2630 (#2677) 2025-12-10 15:34:23 +01:00
Huang Xin 669d3950e2 chore: repackaging readest koplugin for updater, closes #2669 (#2676) 2025-12-10 13:37:32 +01:00
Huang Xin b95895cecf compat(opds): fallback to Basic auth if no WWW-Authenticate challenge in the response headers, closes #2656 (#2673) 2025-12-10 09:57:44 +01:00
Huang Xin 80e11bb0ce refactor(layout): refactor page margins for pixel precision, closes #2652 (#2663) 2025-12-09 16:19:14 +01:00
Huang Xin de3a539621 fix(footnote): add custom attributes for footnote in sanitizer, closes #2651 (#2657) 2025-12-09 05:27:52 +01:00
jacobi petrucciani b425bfdc89 chore: add rust and node deps to the nix devshell (#2655) 2025-12-09 04:44:53 +01:00
Huang Xin 1d1fbdffdb fix(macOS): delay writing to clipboard to ensure it won't be overridden by system clipboard actions, closes #2647 (#2649) 2025-12-08 14:05:27 +01:00
Huang Xin 50cd7f80c6 feat: refresh account info after managing cloud storage (#2648) 2025-12-08 13:13:20 +01:00
Huang Xin 6eb7d91122 release: version 0.9.95 (#2646) 2025-12-08 08:59:21 +01:00
Huang Xin 1fb468b3a6 fix(epub): support SVG cover for ebooks from standardebooks.org (#2645) 2025-12-08 08:52:17 +01:00
Huang Xin 3c7d95cf10 fix(footnote): responsive popup size so that on small screen it won't overflow (#2644) 2025-12-08 07:40:30 +01:00
Huang Xin ba3f060cc4 feat: add support for importing from a directory recursively, closes #179 (#2642) 2025-12-08 07:16:57 +01:00
Huang Xin 11bc7497e8 feat: add support for renaming bookshelf groups (#2639) 2025-12-07 10:04:23 +01:00
Huang Xin fb5d149413 fix: enable shared-intent event listener only on Android for now (#2638) 2025-12-07 06:45:56 +01:00
Huang Xin 42b47d73b7 feat: support cloud storage management (#2636) 2025-12-06 20:25:40 +01:00
Huang Xin 00f36af03a feat(opds): add support to search in OPDS, closes #2598 (#2634) 2025-12-06 12:13:45 +01:00
Huang Xin b78466ca93 fix(opds): relax img-src CSP to support images served from arbitrary HTTP/HTTPS hosts and ports, closes #2631 (#2633) 2025-12-06 04:22:15 +01:00
Huang Xin 4e6f146b8f feat(android): support opening shared files from other apps, closes #2484 (#2628) 2025-12-05 18:13:06 +01:00
Huang Xin cbdd4940d0 fix(android): intercept back button press for Android 15+, closes #2454 (#2626) 2025-12-05 16:27:09 +01:00
Huang Xin d022cb984a chore: bump various dependencies (#2624) 2025-12-05 07:48:03 +01:00
Huang Xin 8de6fa267e fix(cache): invalidate config and doc cache, closes #2595 and closes #2572 (#2623) 2025-12-05 07:03:57 +01:00
Huang Xin b08b7de8e9 fix(tts): fixed highlighting of current sentence for native tts on Android, closes #2620 (#2621) 2025-12-05 04:05:18 +01:00
Huang Xin a232a39f0e fix(pdf): Fixed zoomed layout and hand tool event handling, closes #2596 (#2617) 2025-12-04 19:22:01 +01:00
Huang Xin fad7966fc4 fix(layout): auto two-column layout for unfolded screen, closes #2588 (#2615) 2025-12-04 06:56:23 +01:00
Huang Xin a1487fd60c fix: get rid of the context menu for touch screen or stylus device when selecting text, closes #2579 (#2614) 2025-12-04 06:04:06 +01:00
Huang Xin 9606e315d4 fix(layout): hide overflow of children elements in duokan bleed, closes #2597 (#2613) 2025-12-04 03:40:43 +01:00
Huang Xin 978673268b chore: bump next.js to version 16.0.7 (#2612) 2025-12-04 02:34:10 +01:00
Huang Xin 70158a7f15 refactor(opds): use catalog id instead of credentials in url params, closes #2599 (#2606) 2025-12-03 08:50:55 +01:00
Huang Xin 18d65a2c5b fix(annotator): don't copy selection to notebook with keyboard shortcut by default, closes #2603 (#2605) 2025-12-03 07:08:00 +01:00
Huang Xin 1b0c2afad7 fix(layout): fixed scrollable layout in the about readest window, closes #2593 (#2604) 2025-12-03 06:23:02 +01:00
Huang Xin cef444d374 fix: disable saving last book cover with playstore variant, closes #2600 (#2602) 2025-12-03 05:41:44 +01:00
Huang Xin 75f6efe27a compat(opds): add User-Agent header to fix downloads from Calibre Web OPDS (#2592) 2025-12-02 10:27:33 +01:00
Huang Xin 852f9f40ec chore: fix cross compiling of thumbnail extension (#2587) 2025-12-02 02:27:03 +08:00
Huang Xin b9dadc0f4f chore: update flathub metainfo (#2586) 2025-12-01 18:07:29 +01:00
1116 changed files with 211884 additions and 20211 deletions
+31
View File
@@ -0,0 +1,31 @@
# Dependencies
node_modules
**/node_modules
# Rust build artifacts
target
**/target
# Git
.git
.gitignore
# Build outputs
.next
**/.next
out
**/out
# IDE
.idea
.vscode
*.swp
# OS files
.DS_Store
Thumbs.db
# Logs
*.log
npm-debug.log*
+1 -1
View File
@@ -8,6 +8,6 @@ updates:
groups:
github-actions:
patterns:
- "*" # Group all Actions updates into a single larger pull request
- '*' # Group all Actions updates into a single larger pull request
schedule:
interval: weekly
+103
View File
@@ -0,0 +1,103 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: 'CodeQL Advanced'
on:
push:
branches: ['main']
pull_request:
branches: ['main']
schedule:
- cron: '38 20 * * 4'
jobs:
analyze:
name: Analyze (${{ matrix.language }})
# Runner size impacts CodeQL analysis time. To learn more, please see:
# - https://gh.io/recommended-hardware-resources-for-running-codeql
# - https://gh.io/supported-runners-and-hardware-resources
# - https://gh.io/using-larger-runners (GitHub.com only)
# Consider using larger runners or machines with greater resources for possible analysis time improvements.
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
permissions:
# required for all workflows
security-events: write
# required to fetch internal or private CodeQL packs
packages: read
# only required for workflows in private repositories
actions: read
contents: read
strategy:
fail-fast: false
matrix:
include:
- language: actions
build-mode: none
- language: javascript-typescript
build-mode: none
- language: rust
build-mode: none
# CodeQL supports the following values keywords for 'language': 'actions', 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'rust', 'swift'
# Use `c-cpp` to analyze code written in C, C++ or both
# Use 'java-kotlin' to analyze code written in Java, Kotlin or both
# Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
# To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,
# see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
# If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
steps:
- name: Checkout repository
uses: actions/checkout@v6
# Add any setup steps before running the `github/codeql-action/init` action.
# This includes steps like installing compilers or runtimes (`actions/setup-node`
# or others). This is typically only required for manual builds.
# - name: Setup runtime (example)
# uses: actions/setup-example@v1
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
# If the analyze step fails for one of the languages you are analyzing with
# "We were unable to automatically build your code", modify the matrix above
# to set the build mode to "manual" for that language. Then modify this step
# to build your code.
# ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
- name: Run manual build steps
if: matrix.build-mode == 'manual'
shell: bash
run: |
echo 'If you are using a "manual" build mode for one or more of the' \
'languages you are analyzing, replace this with the commands to build' \
'your code, for example:'
echo ' make bootstrap'
echo ' make release'
exit 1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4
with:
category: '/language:${{matrix.language}}'
+136 -22
View File
@@ -1,5 +1,7 @@
name: PR checks
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
@@ -10,21 +12,30 @@ jobs:
runs-on: ubuntu-latest
env:
RUSTFLAGS: '-C target-cpu=skylake'
SCCACHE_GHA_ENABLED: 'true'
RUSTC_WRAPPER: sccache
steps:
- uses: actions/checkout@v6
with:
submodules: 'true'
- name: setup sccache
uses: mozilla-actions/sccache-action@v0.0.10
- name: Install minimal stable with clippy and rustfmt
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: stable
override: true
components: rustfmt, clippy
- name: Cache apt packages
uses: actions/cache@v5
with:
path: /var/cache/apt/archives
key: apt-rust-lint-${{ runner.os }}
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y pkg-config libfontconfig-dev libglib2.0-dev libgtk-3-dev libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev libsoup-3.0-dev
- name: Format
- name: Format check
working-directory: apps/readest-app/src-tauri
run: cargo fmt --check
- name: Clippy Check
@@ -33,59 +44,162 @@ jobs:
build_web_app:
runs-on: ubuntu-latest
strategy:
matrix:
config:
- platform: 'web'
- platform: 'tauri'
steps:
- uses: actions/checkout@v6
with:
submodules: 'true'
- name: setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10.14.0
uses: pnpm/action-setup@v6
- name: setup node
uses: actions/setup-node@v6
with:
node-version: 22
node-version: 24
cache: pnpm
- name: cache Next.js build
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: apps/readest-app/.next/cache
key: nextjs-${{ matrix.config.platform }}-${{ github.sha }}-${{ hashFiles('pnpm-lock.yaml') }}
key: nextjs-web-${{ github.sha }}-${{ hashFiles('pnpm-lock.yaml') }}
restore-keys: |
nextjs-${{ matrix.config.platform }}-${{ github.sha }}-
nextjs-${{ matrix.config.platform }}-
nextjs-web-${{ github.sha }}-
nextjs-web-
- name: install Dependencies
working-directory: apps/readest-app
run: |
pnpm install && pnpm setup-vendors
- name: run tests
working-directory: apps/readest-app
- name: install LuaJIT (for koplugin lint)
run: sudo apt-get update && sudo apt-get install -y luajit
- name: run format check
run: |
pnpm test -- --watch=false
pnpm format:check || (pnpm format && git diff && exit 1)
- name: run lint
working-directory: apps/readest-app
run: |
pnpm lint
- name: build the web App
if: matrix.config.platform == 'web'
- name: build the web app
working-directory: apps/readest-app
run: |
pnpm build-web && pnpm check:all
- name: build the Tauri App
if: matrix.config.platform == 'tauri'
test_web_app:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
submodules: 'true'
- name: setup pnpm
uses: pnpm/action-setup@v6
- name: setup node
uses: actions/setup-node@v6
with:
node-version: 24
cache: pnpm
- name: cache Next.js build
uses: actions/cache@v5
with:
path: apps/readest-app/.next/cache
key: nextjs-test-${{ github.sha }}-${{ hashFiles('pnpm-lock.yaml') }}
restore-keys: |
nextjs-test-${{ github.sha }}-
nextjs-test-
- name: install Dependencies
working-directory: apps/readest-app
run: |
pnpm build && pnpm check:all
pnpm install && pnpm setup-vendors
- name: install LuaJIT + busted (for koplugin tests)
run: |
sudo apt-get update
# luajit — pnpm test:lua
# luarocks/libsqlite3-dev — required to build lsqlite3complete
sudo apt-get install -y luajit luarocks libsqlite3-dev
# Install busted + the SQLite binding the LibraryStore specs use,
# both pinned to Lua 5.1 (LuaJIT-compatible). System-wide install
# so `luarocks --lua-version=5.1 path` (sourced by
# scripts/test-koplugin.mjs) picks them up.
sudo luarocks --lua-version=5.1 install busted
sudo luarocks --lua-version=5.1 install lsqlite3complete
- name: install playwright browsers
working-directory: apps/readest-app
run: npx playwright install --with-deps chromium
- name: run web tests
working-directory: apps/readest-app
run: pnpm test:pr:web
- name: run koplugin tests
working-directory: apps/readest-app
run: pnpm test:lua
build_tauri_app:
runs-on: ubuntu-latest
env:
SCCACHE_GHA_ENABLED: 'true'
RUSTC_WRAPPER: sccache
steps:
- uses: actions/checkout@v6
with:
submodules: 'true'
- name: setup pnpm
uses: pnpm/action-setup@v6
- name: setup node
uses: actions/setup-node@v6
with:
node-version: 24
cache: pnpm
- name: cache Next.js build
uses: actions/cache@v5
with:
path: apps/readest-app/.next/cache
key: nextjs-tauri-${{ github.sha }}-${{ hashFiles('pnpm-lock.yaml') }}
restore-keys: |
nextjs-tauri-${{ github.sha }}-
nextjs-tauri-
- name: install Dependencies
working-directory: apps/readest-app
run: |
pnpm install && pnpm setup-vendors
- name: setup sccache
uses: mozilla-actions/sccache-action@v0.0.10
- name: install Rust toolchain
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: stable
- uses: Swatinem/rust-cache@v2
with:
key: tauri-cargo
cache-all-crates: 'true'
- name: Cache apt packages
uses: actions/cache@v5
with:
path: /var/cache/apt/archives
key: apt-tauri-${{ runner.os }}
- name: install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y pkg-config libfontconfig-dev libglib2.0-dev libgtk-3-dev libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev libsoup-3.0-dev xvfb
- name: run tauri tests
working-directory: apps/readest-app
run: xvfb-run pnpm test:pr:tauri
+76 -19
View File
@@ -24,7 +24,7 @@ jobs:
run: echo "PACKAGE_VERSION=$(node -p "require('./apps/readest-app/package.json').version")" >> $GITHUB_ENV
- name: get release
id: get-release
uses: actions/github-script@v8
uses: actions/github-script@v9
with:
script: |
const { data } = await github.rest.repos.getLatestRelease({
@@ -35,7 +35,7 @@ jobs:
core.setOutput('release_tag', data.tag_name);
- name: get release notes
id: get-release-notes
uses: actions/github-script@v8
uses: actions/github-script@v9
with:
script: |
const fs = require('fs');
@@ -57,7 +57,7 @@ jobs:
steps:
- name: update release
id: update-release
uses: actions/github-script@v8
uses: actions/github-script@v9
env:
release_id: ${{ needs.get-release.outputs.release_id }}
release_tag: ${{ needs.get-release.outputs.release_tag }}
@@ -69,7 +69,7 @@ jobs:
repo: context.repo.repo,
tag_name: process.env.release_tag,
})
const notes = process.env.release_note.split(/(?:\d\.\s)/).filter(Boolean);
const notes = process.env.release_note.split(/\d+\.\s/).filter(Boolean);
const formattedNotes = notes.map(note => `* ${note.trim()}`).join("\n");
const body = `## Release Highlight\n${formattedNotes}\n\n${data.body}`;
github.rest.repos.updateRelease({
@@ -95,10 +95,23 @@ jobs:
run: |
version=${{ needs.get-release.outputs.release_version }}
plugin_zip="Readest-${version}-1.koplugin.zip"
meta_file="apps/readest.koplugin/_meta.lua"
perl -i -pe "s/^}/ version = \"${version}\",\n}/" "${meta_file}"
cd apps/readest.koplugin
zip -r ../../${plugin_zip} .
cd ../..
# Exclude dev-only artifacts from the published plugin zip:
# scripts/ — i18n + build helpers
# docs/ — design notes
# spec/ — busted test suite
# .busted — busted runner config
# Mirror these in apps/readest.koplugin/scripts/build-koplugin.mjs
# for local builds.
cd apps
zip -r ../${plugin_zip} readest.koplugin \
-x 'readest.koplugin/scripts/*' \
'readest.koplugin/docs/*' \
'readest.koplugin/spec/*' \
'readest.koplugin/.busted'
cd ..
echo "Uploading ${plugin_zip} to GitHub release"
gh release upload ${{ needs.get-release.outputs.release_tag }} ${plugin_zip} --clobber
@@ -122,11 +135,6 @@ jobs:
release: linux
arch: aarch64
rust_target: aarch64-unknown-linux-gnu
- os: ubuntu-22.04-arm
release: linux
arch: armhf
rust_target: arm-unknown-linux-gnueabihf
args: '--target arm-unknown-linux-gnueabihf --bundles deb'
- os: macos-latest
release: macos
arch: aarch64
@@ -152,14 +160,12 @@ jobs:
run: git submodule update --init --recursive
- name: setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10.14.0
uses: pnpm/action-setup@v6
- name: setup node
uses: actions/setup-node@v6
with:
node-version: 22
node-version: 24
cache: pnpm
- name: setup Java (for Android build only)
@@ -171,7 +177,7 @@ jobs:
- name: setup Android SDK (for Android build only)
if: matrix.config.release == 'android'
uses: android-actions/setup-android@v3
uses: android-actions/setup-android@v4
- name: install NDK (for Android build only)
if: matrix.config.release == 'android'
@@ -196,7 +202,7 @@ jobs:
if: contains(matrix.config.os, 'ubuntu') && matrix.config.release != 'android' && matrix.config.arch != 'armhf'
run: |
sudo apt-get update
sudo apt-get install -y pkg-config libfontconfig-dev libgtk-3-dev libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf xdg-utils
sudo apt-get install -y pkg-config libfontconfig-dev libgtk-3-dev libwebkit2gtk-4.1 libwebkit2gtk-4.1-dev libjavascriptcoregtk-4.1 libjavascriptcoregtk-4.1-dev gir1.2-javascriptcoregtk-4.1 gir1.2-webkit2-4.1 libappindicator3-dev librsvg2-dev patchelf xdg-utils
- name: install dependencies (ubuntu only - armhf specific)
if: contains(matrix.config.os, 'ubuntu') && matrix.config.arch == 'armhf'
@@ -208,6 +214,7 @@ jobs:
echo 'PKG_CONFIG_PATH=/usr/lib/arm-linux-gnueabihf/pkgconfig:/usr/share/pkgconfig' >> $GITHUB_ENV
echo 'PKG_CONFIG_SYSROOT_DIR=/usr/arm-linux-gnueabihf' >> $GITHUB_ENV
echo 'CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc' >> $GITHUB_ENV
echo 'CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABIHF_RUSTFLAGS=--cfg=io_uring_skip_arch_check' >> $GITHUB_ENV
- name: create .env.local file for Next.js
run: |
@@ -284,10 +291,15 @@ jobs:
echo "Uploading updated latest.json to GitHub release"
gh release upload ${{ needs.get-release.outputs.release_tag }} latest.json --clobber
- name: Override tauri-cli with custom AppImage format (Linux)
if: matrix.config.release == 'linux'
run: cargo install tauri-cli --git https://github.com/tauri-apps/tauri --branch feat/truly-portable-appimage --force
- uses: tauri-apps/tauri-action@v0
if: matrix.config.release != 'android'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_BUNDLER_NEW_APPIMAGE_FORMAT: 'true'
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
@@ -314,6 +326,8 @@ jobs:
if: matrix.config.os == 'windows-latest'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
shell: bash
@@ -321,7 +335,7 @@ jobs:
echo "Building Portable Binaries"
pushd apps/readest-app/
echo "NEXT_PUBLIC_PORTABLE_APP=true" >> .env.local
pnpm tauri build
pnpm tauri build ${{ matrix.config.args }}
popd
echo "Uploading Portable Binaries"
@@ -346,8 +360,51 @@ jobs:
echo "Uploading $bin_file to GitHub release"
gh release upload ${{ needs.get-release.outputs.release_tag }} $bin_file --clobber
echo "Signing portable binary"
pushd apps/readest-app/
pnpm tauri signer sign "../../$bin_file"
popd
echo "Uploading signature to GitHub release"
gh release upload ${{ needs.get-release.outputs.release_tag }} $bin_file.sig --clobber
- name: download and update latest.json for Windows portable release
if: matrix.config.os == 'windows-latest'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
shell: bash
run: |
curl -sL https://github.com/readest/readest/releases/latest/download/latest.json -o latest.json
version=${{ needs.get-release.outputs.release_version }}
arch=${{ matrix.config.arch }}
if [ "$arch" = "x86_64" ]; then
bin_file="Readest_${version}_x64-portable.exe"
platform_key="windows-x86_64-portable"
elif [ "$arch" = "aarch64" ]; then
bin_file="Readest_${version}_arm64-portable.exe"
platform_key="windows-aarch64-portable"
else
echo "Unknown architecture: $arch"
exit 1
fi
portable_url="https://github.com/readest/readest/releases/download/${{ needs.get-release.outputs.release_tag }}/$bin_file"
portable_sig=$(cat $bin_file.sig)
jq --arg url "$portable_url" \
--arg sig "$portable_sig" \
--arg key "$platform_key" \
'.platforms[$key] = {signature: $sig, url: $url}' latest.json > tmp.$$.json && mv tmp.$$.json latest.json
echo "Uploading updated latest.json to GitHub release"
gh release upload ${{ needs.get-release.outputs.release_tag }} latest.json --clobber
upload-to-r2:
needs: [get-release, build-tauri]
permissions:
contents: read
actions: write
uses: ./.github/workflows/upload-to-r2.yml
with:
tag: ${{ needs.get-release.outputs.release_tag }}
+2
View File
@@ -8,6 +8,8 @@ on:
jobs:
rerun:
runs-on: ubuntu-latest
permissions:
actions: write
steps:
- name: rerun ${{ inputs.run_id }}
env:
+4
View File
@@ -17,6 +17,8 @@ on:
jobs:
upload-to-r2:
runs-on: ubuntu-latest
permissions:
contents: read
timeout-minutes: 3
strategy:
fail-fast: false
@@ -70,6 +72,8 @@ jobs:
if: failure() && fromJSON(github.run_attempt) < 3
needs: [upload-to-r2]
runs-on: ubuntu-latest
permissions:
actions: write
steps:
- env:
GH_REPO: ${{ github.repository }}
+1 -1
View File
@@ -14,7 +14,7 @@ jobs:
- uses: actions/checkout@v6
with:
submodules: 'true'
- uses: amondnet/vercel-action@v25
- uses: amondnet/vercel-action@v42
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
github-token: ${{ secrets.GITHUB_TOKEN }}
+11
View File
@@ -1,4 +1,5 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
docker/.env
# dependencies
/node_modules
@@ -40,6 +41,16 @@ next-env.d.ts
target
fastlane/report.xml
fastlane/metadata/android/en-US/changelogs
*.koplugin.zip
# nix
result*
.playwright-mcp/
.gstack
.claude/worktrees
.claude/settings.local.json
+12
View File
@@ -10,3 +10,15 @@
[submodule "packages/simplecc-wasm"]
path = packages/simplecc-wasm
url = https://github.com/readest/simplecc-wasm.git
[submodule "apps/readest-app/src-tauri/plugins/tauri-plugin-turso"]
path = apps/readest-app/src-tauri/plugins/tauri-plugin-turso
url = https://github.com/readest/tauri-plugin-turso.git
[submodule "apps/readest-app/.claude/skills/gstack"]
path = apps/readest-app/.claude/skills/gstack
url = https://github.com/garrytan/gstack.git
[submodule "packages/qcms"]
path = packages/qcms
url = https://github.com/mozilla/pdf.js.qcms.git
[submodule "packages/js-mdict"]
path = packages/js-mdict
url = https://github.com/readest/js-mdict.git
+1
View File
@@ -0,0 +1 @@
pnpm exec lint-staged
+2
View File
@@ -0,0 +1,2 @@
pnpm -C apps/readest-app lint
pnpm -C apps/readest-app test
+38 -1
View File
@@ -1 +1,38 @@
packages/foliate-js/
# Dependencies
node_modules
pnpm-lock.yaml
# Build Artifacts (Web & Rust)
.next
.open-next
.build
.tauri
out
build
dist
target
fastlane
.wrangler
# Autogenerated Tauri files
gen
**/autogenerated
**/schemas
# Submodules (External Repos)
packages
# Claude Code Skills & Config
apps/readest-app/.claude
# Vendored Assets (Generated/External Code)
apps/readest-app/public/*.js
apps/readest-app/public/vendor
apps/readest-app/src-tauri/plugins/tauri-plugin-turso
# Environment & Editor
.env
.env.*
.vscode
.idea
*.log
+16 -1
View File
@@ -13,4 +13,19 @@
"javascript.validate.enable": false,
"javascript.format.enable": false,
"typescript.format.enable": false,
}
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
},
"[css]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"prettier.requireConfig": true,
"prettier.documentSelectors": ["**/*.{js,jsx,ts,tsx,css,json,md,html,yml}"]
}
+2 -2
View File
@@ -43,8 +43,8 @@ git submodule update --init --recursive
```bash
# might need to rerun this when code is updated
pnpm install
# copy pdfjs-dist to Next.js public directory
pnpm --filter @readest/readest-app setup-pdfjs
# copy vendors dist libs to public directory
pnpm --filter @readest/readest-app setup-vendors
```
#### 3. Verify Dependencies Installation
Generated
+3965 -1036
View File
File diff suppressed because it is too large Load Diff
+9 -4
View File
@@ -2,8 +2,10 @@
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"
]
exclude = [
"packages/qcms"
]
resolver = "2"
@@ -19,8 +21,12 @@ schemars = "0.8"
serde_json = "1"
thiserror = "2"
glob = "0.3"
zbus = "5.9"
dunce = "1"
url = "2"
tar = "0.4.45"
nix = "0.20.2"
glib = "0.20.0"
[workspace.package]
authors = ["Bilingify LLC"]
@@ -33,5 +39,4 @@ rust-version = "1.77.2"
[patch.crates-io]
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" }
+33 -31
View File
@@ -1,38 +1,40 @@
FROM node:22-slim
ENV PNPM_HOME="/root/.local/share/pnpm"
ENV PATH="${PATH}:${PNPM_HOME}"
RUN npm install --global pnpm
# Install necessary packages
RUN apt update -y && apt install -y --no-install-recommends \
libwebkit2gtk-4.1-dev \
build-essential \
curl \
wget \
file \
libxdo-dev \
libssl-dev \
libayatana-appindicator3-dev \
librsvg2-dev \
ca-certificates \
&& apt-get clean && rm -rf /var/lib/apt/lists/*
# Install Rust and Cargo
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
ENV PATH="/root/.cargo/bin:${PATH}"
COPY . /app
FROM docker.io/node:22-slim AS base
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
RUN corepack prepare pnpm@10.29.3 --activate
WORKDIR /app
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
COPY apps/readest-app/package.json ./apps/readest-app/
COPY patches/ ./patches/
COPY packages/ ./packages/
RUN pnpm install
RUN pnpm --filter @readest/readest-app setup-pdfjs
FROM base AS dependencies
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
RUN pnpm --filter @readest/readest-app setup-vendors
FROM dependencies AS development-stage
COPY . .
WORKDIR /app/apps/readest-app
EXPOSE 3000
ENTRYPOINT ["pnpm", "dev-web", "-H", "0.0.0.0"]
FROM base AS build
ARG NEXT_PUBLIC_SUPABASE_URL
ARG NEXT_PUBLIC_SUPABASE_ANON_KEY
ARG NEXT_PUBLIC_APP_PLATFORM
ARG NEXT_PUBLIC_API_BASE_URL
ARG NEXT_PUBLIC_OBJECT_STORAGE_TYPE
ARG NEXT_PUBLIC_STORAGE_FIXED_QUOTA
ARG NEXT_PUBLIC_TRANSLATION_FIXED_QUOTA
COPY --from=dependencies /app/node_modules /app/node_modules
COPY --from=dependencies /app/apps/readest-app/node_modules /app/apps/readest-app/node_modules
COPY --from=dependencies /app/apps/readest-app/public/vendor /app/apps/readest-app/public/vendor
COPY --from=dependencies /app/packages/foliate-js/node_modules /app/packages/foliate-js/node_modules
COPY . .
WORKDIR /app/apps/readest-app
RUN pnpm build-web
ENTRYPOINT ["pnpm", "start-web"]
FROM build as production-stage
ENTRYPOINT ["pnpm", "start-web", "-H", "0.0.0.0"]
EXPOSE 3000
+67 -32
View File
@@ -45,38 +45,38 @@
<div align="left">✅ Implemented</div>
| **Feature** | **Description** | **Status** |
| --------------------------------------- | -------------------------------------------------------------------------------------------------------------- | ---------- |
| **Multi-Format Support** | Support EPUB, MOBI, KF8 (AZW3), FB2, CBZ, TXT, PDF (experimental) | ✅ |
| **Scroll/Page View Modes** | Switch between scrolling or paginated reading modes. | ✅ |
| **Full-Text Search** | Search across the entire book to find relevant sections. | ✅ |
| **Annotations and Highlighting** | Add highlights, bookmarks, and notes to enhance your reading experience. | ✅ |
| **Excerpt Text for Note-Taking** | Easily excerpt text from books for detailed notes and analysis. | ✅ |
| **Dictionary/Wikipedia Lookup** | Instantly look up words and terms when reading. | ✅ |
| **[Parallel Read][link-parallel-read]** | Read two books or documents simultaneously in a split-screen view. | ✅ |
| **Customize Font and Layout** | Adjust font, layout, theme mode, and theme colors for a personalized experience. | ✅ |
| **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. | ✅ |
| **Accessibility** | Provides full keyboard navigation and supports for screen readers such as VoiceOver, TalkBack, NVDA, and Orca. | ✅ |
| **Translate with DeepL and Yandex** | From a single sentence to the entire book—translate instantly. | ✅ |
| **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. | ✅ |
| **Feature** | **Description** | **Status** |
| ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------- | ---------- |
| **Multi-Format Support** | Support EPUB, MOBI, KF8 (AZW3), FB2, CBZ, TXT, PDF | ✅ |
| **Scroll/Page View Modes** | Switch between scrolling or paginated reading modes. | ✅ |
| **Full-Text Search** | Search across the entire book to find relevant sections. | ✅ |
| **Annotations and Highlighting** | Add highlights, bookmarks, and notes to enhance your reading experience and use instant mode for quicker interactions. | ✅ |
| **Dictionary/Wikipedia Lookup** | Instantly look up words and terms when reading. | ✅ |
| **[Parallel Read][link-parallel-read]** | Read two books or documents simultaneously in a split-screen view. | ✅ |
| **Customize Font and Layout** | Adjust font, layout, theme mode, and theme colors for a personalized experience. | ✅ |
| **Code Syntax Highlighting** | Read software manuals with rich coloring of code examples. | ✅ |
| **File Association and Open With** | Quickly open files in Readest in your file browser with one-click. | ✅ |
| **Library Management** | Organize, sort, and manage your entire ebook library. | ✅ |
| **OPDS/Calibre Integration** | Integrate OPDS/Calibre to access online libraries and catalogs. | ✅ |
| **Translate with DeepL and Yandex** | From a single sentence to the entire book—translate instantly. | ✅ |
| **Text-to-Speech (TTS) Support** | Enjoy smooth, multilingual narration—even within a single book. | ✅ |
| **Sync across Platforms** | Synchronize book files, reading progress, notes, and bookmarks across all supported platforms. | ✅ |
| [**Sync with Koreader**][link-kosync-wiki] | Synchronize reading progress, notes, and bookmarks with [Koreader][link-koreader] devices. | ✅ |
| **Accessibility** | Provides full keyboard navigation and supports for screen readers such as VoiceOver, TalkBack, NVDA, and Orca. | ✅ |
| **Visual & Focus Aids** | Reading ruler, paragraph-by-paragraph reading mode, and speed reading features. | ✅ |
## Planned Features
<div align="left">🛠 Building</div>
<div align="left">🔄 Planned</div>
| **Feature** | **Description** | **Priority** |
| ------------------------------------------ | ------------------------------------------------------------------------------------------ | ------------ |
| [**Sync with Koreader**][link-kosync-wiki] | 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. | 🛠 |
| **Support OPDS/Calibre** | Integrate OPDS/Calibre to access online libraries and catalogs. | 🔄 |
| **Audiobook Support** | Extend functionality to play and manage audiobooks. | 🔄 |
| **Handwriting Annotations** | Add support for handwriting annotations using a pen on compatible devices. | 🔄 |
| **Advanced Reading Stats** | Track reading time, pages read, and more for detailed insights. | 🔄 |
| **In-Library Full-Text Search** | Search across your entire ebook library to find topics and quotes. | 🔄 |
| **Feature** | **Description** | **Priority** |
| ------------------------------- | -------------------------------------------------------------------------- | ------------ |
| **AI-Powered Summarization** | Generate summaries of books or chapters using AI for quick insights. | 🛠 |
| **Advanced Reading Stats** | Track reading time, pages read, and more for detailed insights. | 🛠 |
| **Audiobook Support** | Extend functionality to play and manage audiobooks. | 🔄 |
| **Handwriting Annotations** | Add support for handwriting annotations using a pen on compatible devices. | 🔄 |
| **In-Library Full-Text Search** | Search across your entire ebook library to find topics and quotes. | 🔄 |
Stay tuned for continuous improvements and updates! Contributions and suggestions are always welcome—let's build the ultimate reading experience together. 😊
@@ -122,8 +122,8 @@ Stay tuned for continuous improvements and updates! Contributions and suggestion
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.
```bash
nvm install v22
nvm use v22
nvm install v24
nvm use v24
npm install -g pnpm
rustup update
```
@@ -176,7 +176,10 @@ For Android:
```bash
# Initialize the Android environment (run once)
rm apps/readest-app/src-tauri/gen/android
pnpm tauri android init
pnpm tauri icon ../../data/icons/readest-book.png
git checkout apps/readest-app/src-tauri/gen/android
pnpm tauri android dev
# or if you want to dev on a real device
@@ -188,6 +191,7 @@ For iOS:
```bash
# Set up the iOS environment (run once)
pnpm tauri ios init
pnpm tauri icon ../../data/icons/readest-book.png
pnpm tauri ios dev
# or if you want to dev on a real device
@@ -202,6 +206,9 @@ pnpm tauri android build
pnpm tauri ios build
```
Please refer to our release script if you experience any issues:
https://github.com/readest/readest/blob/main/.github/workflows/release.yml
### 6. Setup dev environment with Nix
If you have Nix installed, you can leverage flake to enter a development shell
@@ -250,6 +257,32 @@ Please check the [wiki][link-gh-wiki] of this project for more information on de
- See Issue [readest/readest#358](https://github.com/readest/readest/issues/358) for further details, or head over to our [Discord][link-discord] server and open a support discussion with detailed logs of your environment and the steps youve taken.
### 2. AppImage Launches but Only Shows a Taskbar Icon
On some Arch Linux systems—especially those using Wayland—the Readest AppImage may briefly show an icon in the taskbar and then exit without opening a window.
You might see logs such as:
```
Could not create default EGL display: EGL_BAD_PARAMETER. Aborting...
```
This behavior is usually caused by compatibility issues between the bundled AppImage libraries and the systems EGL / Wayland environment.
**Workaround 1: Launch with LD_PRELOAD (recommended)**
You can preload the system Wayland client library before launching the AppImage:
```
LD_PRELOAD=/usr/lib/libwayland-client.so /path/to/Readest.AppImage
```
This workaround has been confirmed to resolve the issue on affected systems.
**Workaround 2: Use the Flatpak Version**
If you prefer a more reliable out-of-the-box experience on Arch Linux, consider using the [Flatpak build on Flathub][link-flathub] instead. The Flatpak runtime helps avoid system library mismatches and tends to behave more consistently across different Wayland and X11 setups.
## Contributors
Readest is open-source, and contributions are welcome! Feel free to open issues, suggest features, or submit pull requests. Please **review our [contributing guidelines](CONTRIBUTING.md) before you start**. We also welcome you to join our [Discord][link-discord] community for either support or contributing guidance.
@@ -262,13 +295,13 @@ Readest is open-source, and contributions are welcome! Feel free to open issues,
## Support
If Readest has been useful to you, consider supporting its development. You can [become a sponsor on GitHub](https://github.com/sponsors/readest) or just [donate with crypto](https://donate.readest.com). Your contribution helps us squash bugs faster, improve performance, and keep building great features.
If Readest has been useful to you, consider supporting its development. You can [become a sponsor on GitHub](https://github.com/sponsors/readest), [donate via Stripe](https://donate.stripe.com/4gMcN5aZdcE52kW3TFgjC01), or [donate with crypto](https://donate.readest.com). Your contribution helps us squash bugs faster, improve performance, and keep building great features.
### Sponsors
<p align="center">
<a title="Browser testing via LambdaTest" href="https://www.lambdatest.com/?utm_source=readest&utm_medium=sponsor" target="_blank">
<img src="https://www.lambdatest.com/blue-logo.png" style="vertical-align: middle;" width="250" />
<a title="Browser testing via TestMu AI" href="https://www.testmuai.com/?utm_medium=sponsor&utm_source=readest" target="_blank">
<img src="https://raw.githubusercontent.com/readest/readest/refs/heads/main/data/sponsors/testmu-ai-logo.png" style="vertical-align: middle;" width="250" />
</a>
</p>
@@ -291,7 +324,9 @@ The following libraries and frameworks are used in this software:
The following fonts are utilized in this software, either bundled within the application or provided through web fonts:
[Bitter](https://fonts.google.com/?query=Bitter), [Fira Code](https://fonts.google.com/?query=Fira+Code), [Literata](https://fonts.google.com/?query=Literata), [Merriweather](https://fonts.google.com/?query=Merriweather), [Noto Sans](https://fonts.google.com/?query=Noto+Sans), [Roboto](https://fonts.google.com/?query=Roboto), [LXGW WenKai](https://github.com/lxgw/LxgwWenKai), [MiSans](https://hyperos.mi.com/font/en/), [Source Han](https://github.com/adobe-fonts/source-han-sans/), [WenQuanYi Micro Hei](http://wenq.org/wqy2/)
[Bitter](https://fonts.google.com/specimen/Bitter), [Fira Code](https://fonts.google.com/specimen/Fira+Code), [Inter](https://fonts.google.com/specimen/Inter), [Literata](https://fonts.google.com/specimen/Literata), [Merriweather](https://fonts.google.com/specimen/Merriweather), [Noto Sans](https://fonts.google.com/specimen/Noto+Sans), [Roboto](https://fonts.google.com/specimen/Roboto), [LXGW WenKai](https://github.com/lxgw/LxgwWenKai), [MiSans](https://hyperos.mi.com/font/en/), [Source Han](https://github.com/adobe-fonts/source-han-sans/), [WenQuanYi Micro Hei](http://wenq.org/wqy2/)
We would also like to thank the [Web Chinese Fonts Plan](https://chinese-font.netlify.app) for offering open-source tools that enable the use of Chinese fonts on the web.
---
+142
View File
@@ -0,0 +1,142 @@
# Security Policy
## Threat Model
### Overview
Readest is a cross-platform e-reader (macOS, Windows, Linux, Android, iOS, Web) built on Next.js and Tauri. It processes user-supplied ebook files, syncs data to the cloud, integrates with external services (OPDS catalogs, KOReader, DeepL, Yandex), and handles user authentication.
### Assets
| Asset | Description |
| ------------------------------ | ------------------------------------------------------------------------------------ |
| Ebook files | User-uploaded EPUB, MOBI, PDF, and other formats stored locally and in cloud storage |
| Reading progress & annotations | Highlights, bookmarks, and notes synced across devices |
| User credentials | Authentication tokens and session data for cloud sync |
| User preferences & settings | Reading preferences, custom fonts, theme configurations |
| External API keys | Translation service credentials (DeepL, Yandex) configured by users |
### Threat Actors
| Actor | Motivation |
| ----------------------- | ---------------------------------------------------------- |
| Malicious ebook author | Craft a malformed file to exploit the parser or renderer |
| Network attacker (MitM) | Intercept sync traffic to steal credentials or inject data |
| Malicious OPDS server | Serve crafted catalog responses to exploit the client |
| Compromised dependency | Supply chain attack via npm or Cargo ecosystem |
| Unauthorized user | Access another user's synced library or annotations |
### Attack Surfaces & Mitigations
#### 1. Ebook File Parsing
- **Risk:** Malformed EPUB/MOBI/PDF files could trigger parser bugs, path traversal, or script injection via embedded HTML/JS.
- **Mitigations:** Ebook content is rendered in a sandboxed iframe. External script execution is blocked. File parsing is isolated from the main process.
#### 2. Cloud Sync & Authentication
- **Risk:** Credential theft, session hijacking, or unauthorized access to another user's library data.
- **Mitigations:** All sync traffic uses HTTPS/TLS. Authentication tokens are stored securely (OS keychain/secure storage). Server-side authorization ensures users can only access their own data.
#### 3. OPDS / External Catalog Integration
- **Risk:** A malicious OPDS server could serve crafted XML to exploit the parser, or redirect downloads to malicious files.
- **Mitigations:** OPDS responses are parsed defensively. Users explicitly add catalog sources. Downloaded files are treated as untrusted user content.
#### 4. Rendered HTML/JS in Ebook Content
- **Risk:** Embedded JavaScript in EPUB files could attempt XSS or data exfiltration.
- **Mitigations:** Book content is rendered in a sandboxed iframe with scripting restrictions. Navigation outside the book context is blocked.
#### 5. Supply Chain
- **Risk:** Compromised npm or Cargo packages could introduce malicious code.
- **Mitigations:** Dependencies are pinned via `pnpm-lock.yaml` and `Cargo.lock`. Dependabot and GitHub's dependency review are enabled for automated vulnerability detection.
#### 6. Desktop Native Code (Tauri)
- **Risk:** Tauri IPC commands could be abused by malicious web content to access the filesystem or OS APIs.
- **Mitigations:** Tauri's allowlist restricts which IPC commands are exposed. File system access is scoped to the application data directory.
### Out of Scope
- Vulnerabilities in user's operating system or browser outside of Readest's control
- Physical access attacks to a user's device
- Issues in third-party services (DeepL, Yandex, Calibre) themselves
## Supported Versions
Readest does not currently maintain separate release channels. Security updates are provided only for the latest release series.
| Version | Supported |
| ------- | ------------------ |
| 0.10.x | :white_check_mark: |
| < 0.10 | :x: |
## Reporting a Vulnerability
Please report suspected vulnerabilities privately. Do not open a public GitHub
issue or discussion for security-sensitive reports.
Use GitHub's private vulnerability reporting for this repository:
<https://github.com/readest/readest/security/advisories/new>
When submitting a report, include:
- A clear description of the issue and the affected component
- Steps to reproduce, proof of concept, or a minimal test case
- The versions, platforms, or environments you tested
- Any suggested remediation or mitigating details, if available
What to expect after you report:
- We will aim to acknowledge receipt within 3 business days.
- We may contact you for additional details, reproduction steps, or validation.
- If the report is accepted, we will work on a fix and coordinate disclosure.
- If the report is declined, we will explain why, for example if the behavior is
expected, unsupported, or not reproducible.
Please keep vulnerability details private until a fix is available and the
maintainers have approved disclosure.
## Incident Response Plan
When a security vulnerability is confirmed, we follow this process:
### 1. Triage (Day 12)
- Assign a severity level (Critical / High / Medium / Low) based on impact and exploitability.
- Identify affected versions, components, and users.
- Assign an owner responsible for coordinating the response.
### 2. Containment (Day 13)
- Assess whether an immediate mitigation or workaround can be published.
- Limit further exposure where possible (e.g., disable affected features, update dependencies).
### 3. Remediation (Day 314, depending on severity)
- Develop and internally review a fix.
- Validate the fix does not introduce regressions.
- Prepare a patched release and update changelog.
### 4. Disclosure & Release
- Coordinate disclosure timing with the reporter.
- Publish a GitHub Security Advisory with CVE if applicable.
- Release the patched version and notify users via release notes.
### 5. Post-Incident Review
- Document the root cause, timeline, and resolution.
- Update processes or controls to prevent recurrence.
### Severity Definitions
| Severity | Description |
| -------- | --------------------------------------------------------------------- |
| Critical | Remote code execution, full data compromise, or authentication bypass |
| High | Significant data exposure, privilege escalation, or denial of service |
| Medium | Limited data exposure or functionality disruption |
| Low | Minor issues with minimal security impact |
+47
View File
@@ -0,0 +1,47 @@
# Readest Project Memory
## Key Reference Documents
- [Bug Fixing Patterns](bug-patterns.md) - Common bug categories, root causes, and fix strategies
- [CSS & Style Fixes](css-style-fixes.md) - EPUB CSS override patterns and the style.ts pipeline
- [TTS Fixes](tts-fixes.md) - Text-to-Speech architecture and bug patterns
- [Layout & UI Fixes](layout-ui-fixes.md) - Safe insets, z-index, platform-specific UI issues
- [Platform Compat Fixes](platform-compat-fixes.md) - Android, iOS, Linux, macOS platform-specific bugs
- [Annotator & Reader Fixes](annotator-reader-fixes.md) - Highlight, selection, accessibility bugs
## Critical Files (Most Bug-Prone)
- `src/utils/style.ts` - Central EPUB CSS transformation hub (14+ bug fixes)
- `packages/foliate-js/paginator.js` - Page layout, image sizing, backgrounds
- `src/services/tts/TTSController.ts` - TTS state machine, section tracking
- `src/hooks/useSafeAreaInsets.ts` - Safe area inset management
- `src/app/reader/components/FoliateViewer.tsx` - Reader view orchestration
- `src/app/reader/components/annotator/Annotator.tsx` - Annotation lifecycle
## Feature Notes
- [D-pad Navigation](dpad-navigation.md) — Android TV remote / keyboard arrow navigation design, key files, and pitfalls
- [Cloudflare Workers WebSocket](cloudflare-workers-websocket.md) — use fetch() Upgrade pattern (not `ws` npm); CF delivers binary frames as Blob (must serialize async decodes)
- [Share-a-Book Feature (in progress)](share-feature.md) — locked decisions for the /s/{token} share-link feature; plan at ~/.claude/plans/ok-we-will-learn-cosmic-acorn.md
- [readest.koplugin i18n](koplugin-i18n.md) — gettext loader at `apps/readest.koplugin/i18n.lua`, `.po` catalog at `locales/<i18next-code>/translation.po`, extract/apply scripts in `scripts/`
## Patterns
- [Virtuoso + OverlayScrollbars](virtuoso_overlayscrollbars.md) — useOverlayScrollbars hook integration for overlay scrollbars on mobile webviews
- [Design system → DESIGN.md](feedback_design_system_doc.md) — codify recurring UI/UX rules in `apps/readest-app/DESIGN.md`; never `pl/pr/ml/mr/text-left/text-right` (RTL); §5 boxed list anatomy has uniform `min-h-14` rows and chromeless controls
## Architecture Notes
- foliate-js is a git submodule at `packages/foliate-js/`
- Multiview paginator: loads adjacent sections in background, multiple View/Overlayer instances per book
- Style overrides: `getLayoutStyles()` (always), `getColorStyles()` (when overriding color)
- `transformStylesheet()` does regex-based EPUB CSS rewriting at load time
- TTS uses independent section tracking (`#ttsSectionIndex`) decoupled from view
- Safe area insets flow: Native plugin -> useSafeAreaInsets hook -> component styles
- Dropdown menus use `DropdownContext` (not blur-based) for screen reader compat
## Workflow
- [Test file filter](feedback_test_file_filter.md) — use `pnpm test <path>` without `--` to run a single file
- [Always rebase before PR](feedback_pr_rebase.md) — rebase onto origin/main before creating PRs
- [New branch per PR](feedback_pr_new_branch.md) — always create a fresh branch from main for each new PR/issue
- [Upgrade gstack locally](feedback_gstack_upgrade.md) — always upgrade from the project's .claude/skills/gstack, not global
- [No lookbehind regex](feedback_no_lookbehind_regex.md) — never use `(?<=)` or `(?<!)` in JS/TS; build check rejects them
- [Use worktree](feedback_use_worktree.md) — never `git worktree add` directly; always `pnpm worktree:new` before PR review, issue fix, or feature work
- [en/translation.json holds ONLY plural variants + proper nouns](feedback_en_plurals_manual.md) — non-plural strings stay out (defaultValue: key is the en source); plural strings (`_('...', { count })`) need hand-added `_one`/`_other` entries or the singular renders as "1 days"
- [Never push on every change](feedback_dont_push_every_change.md) — hold pushes during active bug iteration; commit locally only until user confirms or work hits a clean done-state
- [No test seams in production code](feedback_no_test_seams_in_prod.md) — production must never import or call `__reset*ForTests`; cross-module test resets belong in the test file's beforeEach/afterEach
@@ -0,0 +1,90 @@
# Annotator & Reader Fixes Reference
## Annotation System Architecture
### Key Components
- `Annotator.tsx` - Annotation lifecycle, popup display, style/color management
- `AnnotationRangeEditor.tsx` - Drag handles for adjusting selection range
- `MagnifierLoupe.tsx` - Magnifying glass during handle drag (mobile only)
- `useTextSelector.ts` - Text selection detection and processing
- `useAnnotationEditor.ts` - Editing existing annotations
- `useInstantAnnotation.ts` - Creating new annotations on selection
### Highlight Rendering
- Highlights rendered by foliate-js `Overlayer` (SVG overlayer in paginator shadow DOM, not iframe)
- Each view in multiview paginator has its own `Overlayer` instance with unique clipPath ID
- `Overlayer.add()` stores range + draw function; `redraw()` recalculates positions from stored ranges
- Colors stored as color names mapped to custom hex via `globalReadSettings.customHighlightColors`
- Sidebar uses `color-mix()` CSS function with custom colors, not Tailwind utility classes (#3273)
- Rounded highlight style supported via `vertical` option passed to overlayer (#3208)
### Multiview Overlayer Pitfalls
- **Duplicate SVG IDs**: Each overlayer creates `<clipPath>` for loupe hole — IDs MUST be unique per instance or `url(#id)` resolves to wrong element, clipping everything
- **docLoadHandler scope**: `FoliateViewer.tsx` re-adds annotations on `load` event — MUST filter by `detail.index` (loaded section), not re-add ALL annotations (overwrites drag edits)
- **MagnifierLoupe lifecycle**: Don't destroy/recreate loupe on every drag tick — `hideLoupe()` should only run on unmount, `showLoupe()` fast path updates position only
- **Stale closures in useTextSelector**: `getProgress()` must be called inside callbacks, not captured at hook top-level (useFoliateEvents deps are `[view]` only)
## Fix History
| Issue | Problem | Root Cause | Fix |
|-------|---------|------------|-----|
| #3286 | Selection stuck on first annotation | `initializedRef` guard blocked re-computation | Remove guard, consolidate style/color effects |
| #3273 | Custom colors not in sidebar | Hardcoded Tailwind classes | Use inline `style` with `color-mix()` |
| #3234 | Letter-by-letter selection on mobile | No word boundary snapping | Add `snapRangeToWords()` using `Intl.Segmenter` |
| #3208 | Hard rectangular highlights | No border radius support | Pass `vertical` option, update foliate-js |
| #3002 | Can't see text under finger | No magnification UI | New `MagnifierLoupe` component using `view.renderer.showLoupe()` |
| #3082 | No page numbers on annotations | `pageNumber` field missing | Add `pageNumber` to BookNote type, compute on create |
| #3225 | Android tools unresponsive | Premature `makeSelection()` call | Remove premature re-selection in Android path |
## Common Annotation Bugs
### Selection Issues
- **Word snapping**: Uses `Intl.Segmenter` with `granularity: 'word'` to snap selection to word boundaries
- **Android re-selection**: Don't call `makeSelection(sel, index, true)` immediately on pointer-up; let the popup flow complete
- **Range editor handles**: Remove `initializedRef` guards that prevent re-computation when switching annotations
### Color/Style Issues
- **Custom colors in sidebar**: Use inline `style={{ backgroundColor: 'color-mix(...)' }}` not Tailwind classes
- **Style synchronization**: Consolidate `selectedStyle` and `selectedColor` into one `useEffect`
- **Switching annotations**: Must call `setShowAnnotPopup(false)` and `setEditingAnnotation(null)` before setting up new annotation
## Reader/Content Fixes
### Progress Display
- Use physical `view.renderer.page` and `view.renderer.pages` for page counts (#3213, #3200)
- Last page shows 100% by fixing boundary condition (#3383)
- FB2 subsections need special handling for progress (#3136)
### Translation View (#3078)
- Problem: Page jumps back during full-text translation
- Root cause: DOM mutations from sequential translation insertions cause paginator relayout
- Fix: Batch DOM updates with 50ms timer, use bounded concurrent queue (max 5), show loading overlay
### TOC Navigation (#3124)
- Problem: Expanding TOC chapter scrolls back to current chapter
- Fix: Only scroll-into-view on navigation, not on expand/collapse
## Accessibility (a11y) Fixes
### Screen Reader (TalkBack) Support
- **Page indicator updates** (#2276): Add focus handlers on `<p>` elements that call `view.goTo(cfi)` to update position
- **Navigation buttons** (#3036): Always show prev/next buttons when screen reader active; `PageNavigationButtons.tsx`
- **Dropdown menus** (#3035): Use `DropdownContext` with overlay dismiss instead of blur-based closing
### Dropdown Architecture for a11y
- `DropdownContext` (`src/context/DropdownContext.tsx`) manages which dropdown is open globally
- Uses `useId()` for unique identification
- One dropdown open at a time
- `<Overlay>` for dismissal (tap/click outside) instead of `onBlur`
- `<details>` element with `open={isOpen}` for semantic structure
- No auto-focus-first-item (conflicts with TalkBack)
## E-ink Readability
- Use `not-eink:` Tailwind variant for colors and opacity (#3258)
- Don't use `text-primary` (blue) or low opacity on e-ink
- Highlights use foreground color in dark mode e-ink (#3299)
## Key Utility Functions
- `snapRangeToWords()` in `src/utils/sel.ts` - Word boundary snapping
- `handleAccessibilityEvents()` in `src/utils/a11y.ts` - Screen reader focus handling
- `color-mix()` CSS function for custom highlight colors with opacity
@@ -0,0 +1,126 @@
# Bug Fixing Patterns & Strategies
## Common Root Cause Categories
### 1. Overly Broad CSS Selectors
**Pattern:** A CSS rule targets too many elements, causing unintended visual side effects.
**Examples:**
- `hr { mix-blend-mode: multiply }` applied to ALL hr elements instead of only decorative ones (#3086)
- `p img { mix-blend-mode }` applied to block images, not just inline (#3112)
- `svg, img { height: auto; width: auto }` overrode explicit HTML width/height attributes (#3274)
- Background-color override applied unconditionally instead of only when user enabled color override (#3316)
**Fix Strategy:** Narrow selectors with class qualifiers (`.background-img`, `.has-text-siblings`) or attribute pseudo-selectors (`:where(:not([width]))`). Check if the rule should be conditional on a user setting.
### 2. Conditional vs Unconditional Style Overrides
**Pattern:** CSS rules meant for "Override Book Color/Layout" mode are placed in the always-active stylesheet.
**Examples:**
- Calibre `.calibre { color: unset }` was in `getLayoutStyles()` instead of `getColorStyles()` (#3448)
- Image background-color override applied without checking `overrideColor` flag (#3316, #3377)
**Fix Strategy:** Move rules to the correct conditional block: `getColorStyles()` for color overrides, `getLayoutStyles()` for layout overrides. Check the `overrideColor`/`overrideLayout` flags.
### 3. Missing EPUB Stylesheet Transformations
**Pattern:** EPUB stylesheets contain CSS that conflicts with app functionality.
**Examples:**
- `user-select: none` prevents text selection (#3370) -> regex replace in `transformStylesheet()`
- `font-family: serif/sans-serif` on body bypasses user font (#3334) -> detect and unset
- Hardcoded Calibre backgrounds persist in dark mode (#3448) -> unset in color override
**Fix Strategy:** Add regex-based transformation passes in `transformStylesheet()` in `style.ts`.
### 4. Stale State / Refs Not Reset
**Pattern:** A `useRef` or state variable is set once and never properly reset, blocking re-entry.
**Examples:**
- TTS `ttsOnRef` prevented restarting TTS from a new location (#3292)
- `initializedRef` in AnnotationRangeEditor prevented handle position updates (#3286)
- `view.tts` not nulled on shutdown prevented clean TTS restart (#3400)
- TTS safety timeout fired after pause, advancing to next sentence (#3244)
**Fix Strategy:** Check all refs/guards in the affected flow. Ensure cleanup in shutdown/unmount. Remove overly aggressive guards that prevent re-entry.
### 5. Platform API Differences
**Pattern:** A Web API behaves differently or is unavailable on certain platforms.
**Examples:**
- `navigator.getGamepads()` returns null on older Android WebView (#3245)
- `CompressionStream` unavailable on some Android versions (#3255)
- `btoa()` throws on non-ASCII characters (#3436)
- View Transitions API unsupported in WebKitGTK/Linux (#3417)
- `document.startViewTransition()` crashes on Linux
**Fix Strategy:** Always check API availability before use. Add fallback paths. Use feature detection, not platform detection when possible.
### 6. Safe Area Inset Issues
**Pattern:** UI elements overlap system bars (status bar, navigation bar, notch) on mobile.
**Examples:**
- Zoom controls behind status bar (#3426)
- Android navigation bar overlap (#3466)
- iPad sidebar insets incorrect (#3395)
- Reader page layout jump after system UI change (#3469)
**Fix Strategy:** Use `gridInsets` and `statusBarHeight` from `useSafeAreaInsets`. Use `env(safe-area-inset-*)` CSS functions. Call `onUpdateInsets()` after system UI visibility changes. See `docs/safe-area-insets.md`.
### 7. Z-Index Layering Issues
**Pattern:** Interactive elements rendered behind other layers, becoming unclickable.
**Examples:**
- Navigation buttons invisible on mobile (#3201) -> added `z-10`
- Annotation nav bar too prominent (#3386) -> reduced from `z-30` to `z-10`
- Page nav buttons behind TTS control (#3184)
**Fix Strategy:** Check z-index ordering. Use minimum necessary z-index. Reference the z-index hierarchy in the codebase.
### 8. Event Handling Race Conditions
**Pattern:** Timing issues between pointer events, native menus, and React state updates.
**Examples:**
- macOS context menu steals pointer event loop (#3324) -> 100ms setTimeout delay
- Traffic light buttons flicker due to timeout race (#3488, #3129)
- Android tool buttons unresponsive due to premature re-selection (#3225)
**Fix Strategy:** Add small delays before native menu calls. Check event state machine consistency. Remove premature re-triggers on Android.
### 9. foliate-js Rendering Issues
**Pattern:** Bugs in the lower-level EPUB renderer (paginator.js, epub.js).
**Examples:**
- Image size not constrained in double-page mode (#3432)
- Background not shown in scrolled mode (#3344)
- Section content cached incorrectly after mode switch (#3242, #3206)
- Swipe sensitivity too low for non-animated paging (#3310)
**Fix Strategy:** Check both `columnize()` and `scrolled()` code paths in paginator.js. Verify CSS variables (`--available-width`, `--available-height`) are computed correctly. Test in both paginated and scrolled modes.
### 10. Progress/Navigation Calculation Errors
**Pattern:** Page counts, progress percentages, or position tracking are wrong.
**Examples:**
- Progress shows 99.9% at last page (#3383) -> boundary condition
- Pages left shows estimated instead of physical count (#3213, #3200)
- FB2 subsection progress wrong (#3136) -> nested structure not handled
- TOC auto-scrolls on expand (#3124) -> scroll-into-view triggered too broadly
**Fix Strategy:** Use physical `view.renderer.page`/`view.renderer.pages` instead of estimated section metadata. Check boundary conditions (0-indexed vs 1-indexed, inclusive vs exclusive).
### 11. Debounced State Stale on User-Initiated Layout Change
**Pattern:** A scroll/resize handler is debounced for performance, but during the debounce window any code path that re-runs layout based on saved state (e.g. `#anchor`, `#primaryIndex`) sees stale values.
**Example:**
- Scrolled-mode toggle reverted to previous chapter (#3987): the paginator's scroll handler is debounced 250 ms, so toggling `flow=scrolled → flow=paginated` within that window made `render() → scrollToAnchor(#anchor)` restore the anchor from before the user scrolled into the next section. Both `#anchor` and `#primaryIndex` were stale together, sending the position back.
**Fix Strategy:** When an external trigger forces a re-render (here, `setAttribute('flow', ...)`), flush the debounced state synchronously *before* changing the layout. In paginator.js this means overriding `setAttribute` and calling `#detectPrimaryView()` + `#getVisibleRange()` while `this.scrolled` is still true.
### 12. Multiview Paginator Side Effects
**Pattern:** The multiview paginator (e925e9d+) loads adjacent sections in background. Events from these loads can interfere with user interactions on the primary section.
**Examples:**
- `load` event from adjacent section triggers `docLoadHandler` which re-adds ALL annotations, overwriting drag edits
- Multiple overlayers with duplicate SVG `<clipPath>` IDs cause `url(#id)` to resolve to wrong element
- `MagnifierLoupe` destroying/recreating body clone on every drag tick triggers ResizeObserver → expand → redraw
**Fix Strategy:** Scope event handlers to the loaded section's index. Use unique IDs for SVG elements across overlayer instances. Minimize iframe DOM mutations during drag operations.
## Debugging Workflow
1. **Identify the category** from the issue description
2. **Check `style.ts`** first for any CSS-related visual bugs
3. **Check foliate-js** for rendering/layout bugs
4. **Check platform-specific code** for mobile/desktop differences
5. **Write a failing test** before implementing the fix
6. **Test in both paginated and scrolled modes** for layout changes
7. **Test on multiple platforms** for any UI change
8. **Run `pnpm build-check`** before submitting
@@ -0,0 +1,73 @@
---
name: Cloudflare Workers WebSocket
description: How to open and read WebSockets from Cloudflare Workers (the Node `ws` package does not work) and the Blob binary-frame gotcha
type: project
originSessionId: ec3d5424-adc2-4fca-836f-df323797489c
---
# Cloudflare Workers WebSocket on readest-app
## Why the Node `ws` package fails
The Node `ws` npm package (used transitively by `isomorphic-ws`) opens WebSockets by calling `http.request({ createConnection })`. The Cloudflare Workers runtime does not implement `options.createConnection`, so any attempt to `new WebSocket(url, { headers })` in a Worker throws:
```
The options.createConnection option is not implemented
```
This applies even with `compatibility_flags = ["nodejs_compat"]`.
## Correct pattern: fetch-based upgrade
On Workers you open a WebSocket by calling `fetch()` with an `Upgrade: websocket` header against the **https://** (not `wss://`) form of the URL. The response has `status === 101` and a non-standard `webSocket` property that must be `accept()`ed before use:
```ts
const upgradeUrl = url.replace(/^wss:\/\//i, 'https://');
const response = (await fetch(upgradeUrl, {
headers: { ...baseHeaders, Upgrade: 'websocket' },
})) as Response & { webSocket?: WebSocket & { accept(): void } };
if (response.status !== 101 || !response.webSocket) {
throw new Error(`WebSocket upgrade failed with status ${response.status}`);
}
const ws = response.webSocket;
ws.addEventListener('message', onMessage);
ws.accept();
ws.send(payload);
```
Detect the Workers runtime with `typeof globalThis.WebSocketPair !== 'undefined'``WebSocketPair` is a Workers-only global.
## Binary frames arrive as Blob (critical)
Cloudflare Workers deliver WebSocket binary frames as **`Blob`** — not `ArrayBuffer` (browsers) and not `Uint8Array` (Node `ws`). Blob decoding is async via `blob.arrayBuffer()`, so:
1. You must serialize decodes through a promise chain to keep frames in receive order — otherwise parallel awaits can merge bytes out of order.
2. Any terminal text message (e.g. Edge TTS's `Path: turn.end`) arrives **synchronously** and will finalize the stream before the in-flight Blob decodes have flushed. Always `await pendingBinary` in the turn.end handler and the close handler before checking whether data was received.
Example skeleton:
```ts
let pending: Promise<void> = Promise.resolve();
const enqueue = (getBuf: () => Promise<ArrayBufferLike> | ArrayBufferLike) => {
pending = pending.then(async () => {
const buf = await getBuf();
appendBinary(buf);
});
};
ws.addEventListener('message', (event) => {
const data = event.data;
if (data instanceof Blob) enqueue(() => data.arrayBuffer());
else if (data instanceof ArrayBuffer) enqueue(() => data);
else if (data instanceof Uint8Array) enqueue(() => data.buffer.slice(
data.byteOffset, data.byteOffset + data.byteLength,
));
// ... handle text path: turn.end
// -> await pending, then resolve
});
```
## Where this is used
`src/libs/edgeTTS.ts` `#fetchEdgeSpeechWs` has three branches: Tauri (plugin-websocket), Cloudflare Workers (fetch upgrade + Blob handling), and browser/Node fallback (`isomorphic-ws`). The route that exercises the CF branch is `src/app/api/tts/edge/route.ts`, hit when the web client falls back from direct `wss://` (which browsers can't set headers on) to the `/api/tts/edge` HTTPS endpoint.
@@ -0,0 +1,74 @@
# CSS & Style Fixes Reference
## The `style.ts` Pipeline (`src/utils/style.ts`)
This is the most bug-prone file in the codebase (14+ fixes). It handles all EPUB CSS transformations.
### Key Functions
#### `getLayoutStyles()`
- Always-active styles applied to every EPUB section
- Controls: line-height, hyphens, image sizing, table display
- Rules here should NOT be conditional on user settings
- Common mistake: putting color-related rules here instead of `getColorStyles()`
#### `getColorStyles()`
- Conditionally applied when user enables "Override Book Color"
- Controls: foreground/background colors, mix-blend modes, image backgrounds
- Gate rules on `overrideColor` flag
#### `transformStylesheet()`
- Regex-based rewriting of EPUB CSS at load time
- Runs on every stylesheet loaded from the EPUB
- Used to neutralize problematic EPUB CSS declarations
#### `applyTableStyle()`
- Post-render function that scales tables to fit available width
- Uses `getComputedStyle()` (not inline `style.width`) to read actual width
- Has two scaling paths: column-width-based and parent-container-based
### Fix History by Issue
| Issue | Problem | Fix in style.ts |
|-------|---------|-----------------|
| #3494 | Line spacing not on `<li>` | Added `li` CSS rule for `line-height` and `hyphens` |
| #3448 | Calibre colors persist | Moved `.calibre` unset to `getColorStyles()`, added `background-color: unset` |
| #3441 | Body padding/margin | Added `padding: unset; margin: unset` to body in `getLayoutStyles()` |
| #3316 | Image bg unconditional | Made `background-color` rule conditional on `overrideColor` |
| #3377 | Image bg override | Same pattern as #3316, only override when `overrideColor` is true |
| #3334 | Generic font-family | `transformStylesheet()` replaces `font-family: serif/sans-serif` with `unset` on body |
| #3370 | user-select: none | `transformStylesheet()` replaces all `user-select: none` with `unset` |
| #3284 | Table scaling | Added fallback when `totalTableWidth` is 0 but parent has width |
| #3351 | Table display broken | Added `display: table !important` to table rule |
| #3274 | Image dimensions | Changed selectors to `:where(:not([width]))` and `:where(:not([height]))` |
| #3205 | Table width reading | Changed from `style.width` to `getComputedStyle().width`, fixed CSS var unit |
| #3112 | Mix-blend on all images | Narrowed selector to `.has-text-siblings` class |
| #3086 | Mix-blend on hr | Narrowed selector to `hr.background-img` |
| #3012 | Vertical alignment | Fixed available dimensions (subtract insets), replaced `100vw/vh` with CSS vars |
### Common Patterns
1. **Adding new element rules:** Copy the pattern from `p` rules (e.g., adding `li` for #3494)
2. **EPUB CSS neutralization:** Add regex in `transformStylesheet()` to replace problematic declarations
3. **Conditional overrides:** Use `overrideColor`/`overrideLayout` flags to gate rules
4. **Selector narrowing:** Use class qualifiers or attribute pseudo-selectors to avoid over-matching
5. **Table fixes:** Always use `getComputedStyle()`, not inline style. Check both width paths.
### CSS Variables from foliate-js
- `--available-width` - Usable content width (set by paginator.js)
- `--available-height` - Usable content height
- `--full-width` - Full viewport width (numeric, multiply by 1px)
- `--full-height` - Full viewport height (numeric, multiply by 1px)
- `--overlayer-highlight-opacity` - Highlight transparency (default 0.3)
### foliate-js Rendering (`packages/foliate-js/paginator.js`)
Key functions:
- `columnize()` - Paginated layout path
- `scrolled()` - Scrolled layout path
- `setImageSize()` - Constrains image dimensions to available space
- `#replaceBackground()` - Transfers EPUB backgrounds to paginator layer
- `snap()` - Swipe gesture detection for page turning
Common issue: A fix applied to `columnize()` but not `scrolled()` (or vice versa). Always check both paths.
@@ -0,0 +1,40 @@
---
name: D-pad Navigation Design
description: Android TV / Bluetooth remote D-pad navigation architecture, key files, and pitfalls encountered during implementation
type: project
---
## D-pad Navigation Architecture
D-pad support enables Bluetooth remote controller navigation on Android TV (and keyboard arrow navigation on desktop).
### Key Files
- `src/app/reader/hooks/useSpatialNavigation.ts` — Reader toolbar D-pad navigation. Left/Right navigates between buttons, Up/Down moves between header↔footer. Auto-focuses first button on show. Uses focus-probe technique for visibility detection.
- `src/app/library/hooks/useSpatialNavigation.ts` — Library grid D-pad navigation. Arrow keys move between BookshelfItem elements. ArrowDown from outside bookshelf (e.g. header) enters the grid via window-level listener.
- `src/helpers/shortcuts.ts``onToggleToolbar` (Enter key) toggles reader toolbar visibility.
- `src/app/reader/hooks/useBookShortcuts.ts``toggleToolbar` handler shows/hides header+footer bars. Skips when a `<button>` is focused (lets native click fire).
- `src/__tests__/hooks/useSpatialNavigation.test.tsx` — Unit tests for reader toolbar navigation.
### Design Decisions
- **No third-party library**: Tried `@noriginmedia/norigin-spatial-navigation` but it failed due to init timing issues (React child effects run before parent effects) and conflicts with the existing `useShortcuts` system. Custom solution is simpler and more reliable.
- **Two `useSpatialNavigation` hooks**: Same name in different directories — library version handles grid navigation, reader version handles toolbar button navigation. Different navigation patterns but same concept.
- **Platform-agnostic hooks**: Both `useSpatialNavigation` hooks work on all platforms, not just Android.
- **Focus-probe for visibility**: `offsetParent` is unreliable for detecting visible buttons (returns null inside `position: fixed` containers on mobile). Instead, try `btn.focus()` and check if `document.activeElement === btn` — this correctly handles all hiding methods (display:none, visibility:hidden, fixed positioning).
### Pitfalls
1. **WebView spatial navigation conflict**: Android WebView has built-in spatial navigation that intercepts D-pad arrow keys and moves DOM focus between `tabIndex>=0` elements. Added `tabIndex={-1}` to non-interactive overlay elements (HeaderBar trigger, ProgressBar, FooterBar trigger, SectionInfo) to prevent focus theft.
2. **`eventDispatcher.dispatchSync` short-circuits**: When multiple handlers are registered for `native-key-down`, the first handler returning `true` stops propagation. The FooterBar's Back handler fires before the Reader's. Both must independently call `blur()` — can't rely on the Reader's handler running.
3. **Must blur on toolbar dismiss**: When Back/Escape dismisses the toolbar, the focused button must be blurred. Otherwise `document.activeElement` remains a hidden button, and `toggleToolbar` skips Enter when `activeElement.tagName === 'BUTTON'`. Blur is called in FooterBar's handleKeyDown (for Back and Escape) and in Reader's handleKeyDown.
4. **Arrow key trapping must use `stopPropagation`**: Without it, arrow keys bubble to `window` where `useShortcuts` handles them as page turns. The toolbar keydown handler on the container div calls `e.stopPropagation()` + `e.preventDefault()` to prevent this.
5. **Library grid needs window-level listener**: The bookshelf container keydown handler only fires when focus is inside it. A separate `window` keydown listener handles ArrowDown from the header into the grid (when focus is outside the container).
6. **Auto-focus race on toolbar show**: Both header and footer bars auto-focus their first button when `isVisible` becomes true simultaneously. The last effect to run wins. This is acceptable — user can navigate between them with Up/Down.
7. **`offsetParent` null in fixed containers**: On mobile, `.footer-bar` uses `position: fixed`. All child buttons have `offsetParent === null`, making `offsetParent`-based visibility checks useless. The focus-probe approach (try focus, check activeElement) is the reliable alternative.
@@ -0,0 +1,49 @@
---
name: Design rules live in DESIGN.md
description: Readest has a design system doc — codify recurring UI/UX rules there, don't just apply them ad-hoc. Memory points at the canonical location and the patterns it covers.
type: feedback
originSessionId: 85757e57-a029-40f8-b098-88039c43514b
---
The project's design system is documented at `apps/readest-app/DESIGN.md`.
**When the user articulates a UI/UX rule** ("X should always Y", "we follow Z
convention"), add it to DESIGN.md so it persists for the team and for future
sessions — don't just apply it inline and move on.
**Why:** Readest's UI is Adwaita-aligned, e-ink-first, cross-platform-aware.
A doc'd system avoids drift across panels and gives reviewers a reference
point. The user explicitly asked to "remember the UI/UX rules somewhere"
when refining the OPDS Integration sub-page.
**How to apply:** When the user surfaces a new rule:
1. Add it to the appropriate `DESIGN.md` section (numbered principles in §2,
anatomy details in §5, anti-patterns in §10).
2. Cross-reference from related sections so it's discoverable from multiple
entry points.
3. Save the actual code change reference if it captures a canonical example.
**Rules already codified there (don't re-invent — reference instead):**
- §2.12.7: surface continuity, color discipline, two-step depth, localized
hover, motion=color, eink-first, focus visibility.
- §2.8: RTL — always use logical properties (`ps`/`pe`/`ms`/`me`/`text-start`
/`text-end`/`border-s`/`border-e`/`start-*`/`end-*`). Never `pl`/`pr`/`ml`
/`mr`/`text-left`/`text-right`/`left-*`/`right-*`. The user is strict on
this — Readest ships RTL languages.
- §2.9: every panel/sub-page must open with title + one-line description.
- §3: surface tier hierarchy (window/view/card → base-200/100-tinted/100).
- §4: action vocabulary (Accent CTA, Suggested, Flat, Pill, Destructive,
ListExtension).
- §5: boxed list anatomy + uniform row height (`min-h-14 items-center`,
never `py-3`) + chromeless controls inside the box + end-aligned values
with custom `<MdArrowDropDown>` icon (don't trust daisyui's bg-image
chevron for trailing-edge alignment).
- §8: e-ink overlay rules.
- §10: anti-pattern catalog with real before/after examples.
**Common file paths to remember:**
- `apps/readest-app/DESIGN.md` — source of truth.
- `apps/readest-app/src/components/settings/SubPageHeader.tsx` — title +
description sub-page primitive that embodies §2.9.
- `apps/readest-app/src/components/settings/integrations/` — the canonical
reference implementation of the boxed-list-with-rows pattern.
- `apps/readest-app/src/styles/globals.css` — eink overlay rules at
`[data-eink='true']`.
@@ -0,0 +1,11 @@
---
name: Don't push on every change
description: Commit when work is done; don't auto-push every iteration during active debugging
type: feedback
originSessionId: 49a72b36-8f45-4a57-87e1-e10563bac47a
---
Don't `git push` after each commit while a bug is being actively iterated on. Commit locally as needed but hold the push.
**Why:** When a fix doesn't actually solve the user-reported bug, every push is wasted CI cycles + remote churn the user has to look past on the PR. The user is testing live and will tell us when something's actually verified.
**How to apply:** During debugging or fix iterations on a single user-reported bug, commit locally only. Push when (a) the user confirms the fix works, (b) the user explicitly asks to push, or (c) we hit a clean done-state on a multi-step task. New commits + lint/test green is not enough.
@@ -0,0 +1,28 @@
---
name: en/translation.json holds ONLY plural variants and proper-noun overrides
description: For non-plural strings, do NOT add to en/translation.json — the source-code key IS the en value via `defaultValue: key`. ONLY plural strings need explicit `_one`/`_other` entries in en, because i18next needs the forms to pick from.
type: feedback
originSessionId: e4ddc690-b1a9-4557-855f-d4e67055824f
---
**Rule:** `public/locales/en/translation.json` is hand-curated and contains essentially two kinds of entries:
1. **Plural variants** (`<base>_one`, `<base>_other`, and locale-specific `_few`/`_many`/etc. as needed by CLDR). i18next MUST find these to know which form to render — without them, `count: 1` falls back to the bare key like `{{count}} days` and renders "1 days" instead of "1 day".
2. **Proper-noun overrides** (e.g., font names like `LXGW WenKai GB Screen`) where the en value differs from the key.
Everything else — ordinary translatable strings like `Sign in to share books` — does NOT belong in en/translation.json. The translation hook calls `t(key, { defaultValue: key, ...options })`, so for any string not in en/translation.json, i18next renders the key itself. That's why a 51-key en file works for a codebase with thousands of `_()` callsites.
**Why en is hand-curated:** the project's `i18next-scanner.config.cjs` lists every locale EXCEPT `en` in its `lngs` array. The scanner generates `__STRING_NOT_TRANSLATED__` placeholders only for the listed locales; `en` is never touched.
**Workflow when adding `_(...)` calls:**
- **Non-plural string** (e.g. `_('Sign in to share books')`): add the `_(...)` call, run `pnpm run i18n:extract`, translate the new placeholders in non-en locales. **Do NOT touch `en/translation.json`** — the key itself is the en value.
- **Plural string** (e.g. `_('{{count}} days', { count: n })`): same as above, PLUS hand-add `<base>_one` and `<base>_other` to `en/translation.json` (and `_few`/`_many`/etc. only if the source language ever needs them, which English doesn't). Convention from existing entries (e.g., `Are you sure to delete {{count}} selected book(s)?_one``Are you sure to delete {{count}} selected book?`): keep `{{count}}` interpolated even in `_one`, and swap any `(s)` placeholder to the proper singular/plural noun.
**Audit script:** walk `src/`, regex-match `_('...', { ..., count: ... })`, for each base key verify both `<base>_one` and `<base>_other` exist in `en/translation.json`. (See conversation history for an implementation.)
**Where this bit us:**
- Initial bug: `_('{{count}} days', { count: n })` rendered "1 days" because en had no `_one` form.
- Audit found 16 missing en plural keys from earlier PRs (OPDS / TTS / dictionary import) silently rendering wrong.
- Then overcorrected and added non-plural keys like `Sign in to share books` to en/translation.json — wrong, breaks the project's convention. en stays clean for non-plural strings.
@@ -0,0 +1,11 @@
---
name: gstack upgrade location
description: Always upgrade gstack from the project directory (.claude/skills/gstack), not from a global install
type: feedback
---
When upgrading gstack, always run the upgrade from the current project's `.claude/skills/gstack` directory (local-git install), not from a global install path.
**Why:** The project uses a local-git gstack install at `apps/readest-app/.claude/skills/gstack`. Previous mistakes upgraded a global copy while the project's local copy stayed outdated.
**How to apply:** When `/gstack-upgrade` is invoked, ensure the `cd` and `git reset --hard origin/main && ./setup` happen inside the project's `.claude/skills/gstack` directory.
@@ -0,0 +1,11 @@
---
name: No lookbehind regex
description: Never use lookbehind assertions in JS/TS code — the build check rejects them for browser compatibility
type: feedback
---
Never use lookbehind regex (`(?<=...)` or `(?<!...)`) in JavaScript/TypeScript source code. Use `(?:^|[^...])` or other alternatives instead.
**Why:** The project has a `check:lookbehind-regex` build check (`pnpm check:all`) that scans the Next.js output chunks and fails if any lookbehind assertions are found. Older WebViews (especially on some Android devices) don't support lookbehinds.
**How to apply:** When writing regex that needs to assert what comes before a match, use a non-capturing group with alternation (e.g., `(?:^|[^a-z-])`) instead of a negative lookbehind (`(?<![a-z-])`). This applies to all `.ts`/`.tsx`/`.js` files that end up in the build output.
@@ -0,0 +1,24 @@
---
name: No test seams in production code
description: Never import or call `__reset*ForTests` (or any test-only helper) from production modules — keep test orchestration on the test side
type: feedback
originSessionId: 49a72b36-8f45-4a57-87e1-e10563bac47a
---
Production code must never import or call functions named `__reset*ForTests`
(or any other test-only seam). If a `__resetXForTests` function in module A
needs to also clear state owned by module B, the test file is responsible
for calling both resets — not module A's reset chaining into module B's.
**Why:** Importing a test-only helper into a production module pulls the
test seam into the prod import graph, blurs the test/prod boundary, and
risks the helper being shipped or mistakenly called at runtime. Caught
once in `src/services/sync/replicaSync.ts` where
`__resetReplicaSyncForTests` had been changed to call
`__resetSettledEventsForTests` from `@/utils/event` for "convenience."
**How to apply:**
- A `__resetXForTests` function should clear ONLY its own module's state.
- If a test needs a coordinated reset across modules, do it in the test
file's `beforeEach` / `afterEach` — call each module's seam directly.
- Never `import { __reset...ForTests }` inside `src/` outside of
`src/__tests__/`. A grep `grep -rn "__reset.*ForTests" src/ --include="*.ts" --include="*.tsx" | grep -v __tests__ | grep -v "^.*export const __reset"` should return zero hits.
@@ -0,0 +1,11 @@
---
name: Always use a new branch for new PRs
description: Each new PR/issue should get its own fresh branch from main, never reuse an existing feature branch
type: feedback
---
Always create a new branch from main for each new PR or issue. Never reuse an existing feature branch for unrelated work.
**Why:** The user corrected this when a storage fix was committed on the `feat/full-sync-annotations` branch instead of a dedicated branch. Mixing unrelated changes on the same branch makes PRs harder to review and manage.
**How to apply:** Before committing fixes, create a new branch like `fix/<topic>` from `origin/main`. Only reuse a branch if the work is directly related to that branch's existing purpose.
@@ -0,0 +1,11 @@
---
name: Always rebase before PR
description: Rebase to origin/main before creating pull requests
type: feedback
---
Always rebase the branch onto origin/main before creating a pull request.
**Why:** The user wants PRs to be up-to-date with main to avoid merge conflicts and keep a clean history.
**How to apply:** Before running `gh pr create`, always run `git fetch origin && git rebase origin/main` first. If there are conflicts, resolve them before proceeding.
@@ -0,0 +1,11 @@
---
name: test-file-filter
description: Use pnpm test/test:browser with path directly (no --) to run a single test file
type: feedback
---
Run a specific test file with `pnpm test <path>` or `pnpm test:browser <path>` — no `--` separator.
**Why:** Adding `--` before the path (e.g. `pnpm test:browser -- <path>`) causes vitest to ignore the file filter and run all test files. Without `--`, pnpm appends the path directly to the vitest command, which correctly filters to that file only.
**How to apply:** Always use `pnpm test src/__tests__/foo.test.ts` or `pnpm test:browser src/__tests__/foo.browser.test.tsx` when verifying a specific test file.
@@ -0,0 +1,18 @@
---
name: Use worktree for PR/issue/feature work
description: Always create a git worktree with pnpm worktree:new before reviewing PRs, fixing issues, or implementing features
type: feedback
originSessionId: 650f8ff2-980d-459f-ad23-ba0af56e28b5
---
Always use `pnpm worktree:new <branch-name|pr-number>` to create an isolated worktree before starting work on:
- Reviewing a GitHub PR (e.g., `pnpm worktree:new 3809`) → worktree at `~/dev/readest-pr-3809`
- Fixing a GitHub issue (e.g., `pnpm worktree:new fix/issue-123`) → worktree at `~/dev/readest-fix-issue-123`
- Implementing a feature request (e.g., `pnpm worktree:new feat/my-feature`) → worktree at `~/dev/readest-feat-my-feature`
Worktree directory convention: `readest-<name>` in the parent of the repo root (`~/dev/`), with slashes replaced by dashes.
Use `pnpm worktree:rm <branch-name|pr-number>` to clean up when done.
**Why:** Keeps the current bare repo branch untouched. Each task gets its own isolated workspace with submodules, dependencies, env files, and vendor assets already set up.
**How to apply:** Before touching any code for a PR review, bug fix, or feature, run `pnpm worktree:new` first. Work inside the new worktree directory (e.g., `~/dev/readest-pr-3809/apps/readest-app/`). Clean up with `pnpm worktree:rm` after merging or finishing.
@@ -0,0 +1,44 @@
---
name: readest.koplugin i18n system
description: Custom gettext loader, .po catalog layout, and extract/apply scripts for the KOReader plugin at apps/readest.koplugin/
type: reference
originSessionId: 08cfd0cd-b710-4674-9c90-d2ae4827d071
---
The KOReader plugin (`apps/readest.koplugin/`) has its own gettext-based i18n system, parallel to but separate from the readest-app i18next setup.
## Loader
- File: `apps/readest.koplugin/i18n.lua` — isolated module, returns a callable table via `setmetatable({}, {__call = ...})`, so `_("msg")` syntax works as a drop-in replacement for `require("gettext")`. Provides `ngettext`/`pgettext`/`npgettext` too. Falls back to KOReader's native `gettext` for missing strings.
- All Lua sources do `local _ = require("i18n")` (not `require("gettext")`).
- **Never rename `i18n.lua` to `gettext.lua`** — it would shadow KOReader's module via `require("gettext")` and break the fallback chain (recursive require / never loaded).
## Catalog layout
- `apps/readest.koplugin/locales/<lang>/translation.po` — mirrors `apps/readest-app/public/locales/<lang>/translation.json` exactly, using **i18next-style codes** (`zh-CN`, not `zh_CN`).
- The loader converts KOReader's locale (e.g. `zh_CN.utf8``zh-CN`) before lookup.
- Fallback chain: full code → base lang (`pt_BR``pt`) → `zh-CN` for any unspecified zh variant.
- Language list is the single source of truth at `apps/readest-app/i18next-scanner.config.cjs` (`options.lngs`) — currently 31 languages.
## Scripts (in `apps/readest.koplugin/scripts/`)
- **`extract-i18n.js`** — primary tool. Run with `node scripts/extract-i18n.js`. Scans `*.lua` for `_("...")`, `_('...')`, and `_([[...]])` (with proper Lua-escape handling), reads each `.po`, **preserves existing translations**, adds new msgids with empty `msgstr`, drops obsolete msgids. Idempotent.
- **`apply-translations.js`** — bulk applier. Reads `/tmp/koplugin-translations/<lang>.json` files (key = msgid, value = translation) and fills empty `msgstr ""` lines only — **never overwrites** existing translations.
## Workflow for adding/changing strings
1. Edit Lua source(s). Use `_("Foo")` or `T(_("Foo %1"), arg)` (`T` from `require("ffi/util").template`) — **never** `_("Foo ") .. arg`, because RTL/verb-final languages can't reorder the placeholder.
2. `node scripts/extract-i18n.js` — adds new empty msgids, drops obsolete.
3. To translate: drop `<lang>.json` files into `/tmp/koplugin-translations/`, then `node scripts/apply-translations.js`.
4. Verify with `luac -p apps/readest.koplugin/*.lua` and re-run `extract-i18n.js` (should report no changes — idempotency check).
## Translation conventions
- Brand names "Readest" and "KOReader" stay untranslated.
- Technical terms ("PDF", "API", "URL", "Supabase", "Hash") generally kept as-is, sometimes transliterated in non-Latin scripts.
- Dialog title = title case (`Sync Info`); menu item label = sentence case (`Sync info`).
- Lower-confidence translations (bo, si, ta, bn, sl, fa) deserve native-speaker review.
## Storage conventions for plugin state
- Global plugin state: `G_reader_settings:saveSetting("readest_sync", settings)` — login tokens, auto-sync flag, etc.
- Per-book state: `ui.doc_settings:readSetting("readest_sync")` table — keys like `meta_hash_v1`, `last_synced_at_config`, `last_synced_at_notes` (seconds since epoch).
@@ -0,0 +1,70 @@
# Layout & UI Fixes Reference
## Safe Area Insets
### Architecture
- Native plugins push inset values: iOS (`NativeBridgePlugin.swift`), Android (`NativeBridgePlugin.kt`)
- `useSafeAreaInsets` hook (`src/hooks/useSafeAreaInsets.ts`) reads and caches values
- Components use `gridInsets` for positioning relative to safe areas
- CSS: `env(safe-area-inset-top/bottom/left/right)` for CSS-level insets
### Common Issues
- **Stale insets after system UI change** (#3469): Call `onUpdateInsets()` after `setSystemUIVisibility()`
- **iPad sidebar insets wrong** (#3395): Different inset handling needed for sidebar vs main view
- **Android nav bar overlap** (#3466): Use `calc(env(safe-area-inset-bottom) + 16px)` for Android bottom padding
- **Zoom controls behind status bar** (#3426): Pass `gridInsets` through component chain, use `Math.max(gridInsets.top, statusBarHeight)`
### Rules (see also `docs/safe-area-insets.md`)
- Always pass `gridInsets` to overlay/floating components near screen edges
- On Android, account for system navigation bar with `env(safe-area-inset-bottom)`
- After toggling system UI visibility, force-refresh insets via `onUpdateInsets()`
## Z-Index Hierarchy
- Navigation buttons: `z-10` (when visible)
- Annotation nav bar: `z-10` (reduced from z-30 in #3386)
- TTS control button: ensure above page nav buttons
- Floating overlays: check against `gridInsets` positioning
## macOS Traffic Lights
- Managed by `trafficLightStore.ts` and `HeaderBar.tsx`
- **Fullscreen check required** (#3129): `await currentWindow.isFullscreen()` before hiding
- **Timeout for visibility toggle** (#3488): Use 100ms delay to prevent flickering
- **Sidebar interaction** (#3488): Check `getIsSideBarVisible()` before hiding traffic lights
- Never hide traffic lights when sidebar is open
## Touch/Input Issues
- **Slider hit area on iOS** (#3382): Use min-h-12, strip browser appearance with CSS
- **Context menu on macOS** (#3324): 100ms delay before `onContextMenu()` to let pointer events complete
- **Swipe sensitivity** (#3310): Use average velocity (distance/time) instead of instantaneous velocity for non-animated paging
- **Touchpad natural scrolling** (#3127): Respect system natural scrolling setting in `usePagination.ts`
## Dialog/Menu Layout
- **Dialog header** (#3352): Use `px-2 sm:pe-3 sm:ps-2` padding to align with border radius
- **Settings alignment** (#3151): Use `Menu` component instead of raw `div` for consistent styling
- **Dropdown for screen readers** (#3035): Use `DropdownContext` with overlay dismiss, not blur-based closing
## Component-Specific Fixes
### HeaderBar.tsx
- Traffic light visibility management
- Sidebar toggle persistent position (#3193)
- Library button placement
### PageNavigationButtons.tsx
- z-10 when visible (#3201)
- Always shown for screen readers (#3036)
- Toggle via `showPageNavButtons` setting
### ProgressInfo.tsx
- Use physical `page`/`pages` from renderer, not estimated values (#3213, #3200)
- CSS classes: `time-left-label`, `pages-left-label`, `progress-info-label` (#3343)
### ReadingRuler.tsx
- Remove `containerStyle` from overlay so dimmed area covers full screen (#3304)
### NavigationBar.tsx
- Handle `gridInsets` internally, not via pre-computed `navPadding` (#3466)
### ContentNavBar.tsx (annotation search results)
- Floating buttons with drop shadow, not full-width bar (#3386)
- z-10 z-index
@@ -0,0 +1,85 @@
# Platform Compatibility Fixes Reference
## Android
### WebView API Issues
- **`navigator.getGamepads()`** returns null on older WebView (#3245): Always null-check before `.some()`
- **`CompressionStream`** unavailable on some Android WebView versions (#3255): Add fallback zip compression path
- **Annotation tools unresponsive** (#3225): Don't call `makeSelection()` immediately on pointer-up; let popup flow complete naturally
- **Safe inset updates** (#3469): Call `onUpdateInsets()` after `setSystemUIVisibility()`
- **Navigation bar overlap** (#3466): Use `calc(env(safe-area-inset-bottom) + 16px)` for bottom padding
### General Android Rules
- Test with older WebView versions (97+)
- Always check API availability before calling Web APIs
- Touch event handling differs from iOS - avoid premature re-selection
## iOS
### Common Issues
- **Slider touch dead zones** (#3382): Strip native appearance, use larger hit areas (min-h-12)
- **Safe area insets stale** (#3395): Native Swift plugin must push updated insets
- **Section content caching** (#3242, #3206): Don't cache section content in foliate-js when updating subitems; cached content retains stale styles after mode switch
- **CompressionStream** (#3255): Also broken on iOS 15.x; zip.js has its own native API disable
- **zip.js native API** (#3170): Disable native `CompressionStream`/`DecompressionStream` on iOS 15.x
### iOS-Specific Code
- `src-tauri/plugins/tauri-plugin-native-bridge/ios/Sources/NativeBridgePlugin.swift`
- Slider CSS: `-webkit-appearance: none; appearance: none` in globals.css
- `useSafeAreaInsets.ts` hook
## macOS
### Traffic Lights (Window Controls)
- Check `isFullscreen()` before hiding (#3129)
- Use timeouts (100ms) for visibility transitions (#3488)
- Don't hide when sidebar is open (#3488)
### Input Issues
- **Context menu steals event loop** (#3324): 100ms setTimeout before calling menu.popup()
- **Touchpad natural scrolling** (#3127): Respect system setting in `usePagination.ts`
- **Two-finger swipe** (#3127): Support trackpad two-finger swipe for pagination
### macOS-Specific Code
- `src-tauri/src/macos/` - Platform-specific Rust code
- `src/store/trafficLightStore.ts`
- `src/hooks/useTrafficLight.ts`
## Linux
### WebKitGTK Issues
- **View Transitions API unsupported** (#3417): Feature-detect `document.startViewTransition` before calling
- Use `useAppRouter.ts` to avoid transitions on Linux
## E-ink Devices
### Legibility Issues
- **Low contrast colors** (#3258): Use `not-eink:` Tailwind variant prefix for colors/opacity
- **Links invisible** (#3258): Don't apply `text-primary` (blue) on e-ink; use default text color
- **Opacity too low** (#3258): Don't apply `opacity-60`/`opacity-75` on e-ink devices
- **Highlight visibility** (#3299): Use foreground color for highlights in dark mode e-ink
### E-ink CSS Pattern
```
// Instead of:
className="text-primary opacity-60"
// Use:
className="not-eink:text-primary not-eink:opacity-60"
```
## Docker/Self-Hosting
- **Missing submodules** (#3233): Run `git submodule update --init --recursive` before build
- Simplecc WASM module must be initialized
## OPDS
- **Non-ASCII credentials** (#3436): Use `TextEncoder` + manual Base64 instead of `btoa()`
- **Author parsing** (#3120): Handle varied metadata structures in OPDS 2.0 feeds
- **Responsive layout** (#3418): Ensure catalog and download button layout works on small screens
## Cross-Platform Testing Checklist
1. Android (old WebView + current)
2. iOS (15.x + current)
3. macOS (traffic lights, trackpad)
4. Linux (WebKitGTK)
5. E-ink devices (contrast, colors)
6. Web (CloudFlare Workers deployment)
@@ -0,0 +1,53 @@
---
name: Share-a-Book Feature (in progress)
description: Active implementation of /s/{token} share links + cherry-picks (CFI auto-include, 1-tap library import, branded OG images). All locked decisions and the plan file path.
type: project
originSessionId: e4ddc690-b1a9-4557-855f-d4e67055824f
---
Active feature on branch `dev` as of 2026-05-02. Plan file: `/Users/chrox/.claude/plans/ok-we-will-learn-cosmic-acorn.md` (always read for source of truth before continuing).
**Why:** complement just-shipped annotation deep-links (PRs #4018, #4019) with whole-book sharing. Cloud-sync infra exists; this layers public time-limited links on top.
**How to apply:** these decisions are locked across CEO + eng + design reviews. Do NOT re-litigate when implementing.
## Locked decisions (authoritative)
- **Universal 7-day cap** on share expiry, no tier differentiation, no "Never". All users pick `[1, 3, 7]` days. DMCA-risk reduction.
- **App Router** for all share routes (mirrors `/o` precedent + recent Stripe / AI / IAP / OPDS). Pages API `storage/*` neighbors stay where they are.
- **Single non-dynamic landing page** at `src/app/s/page.tsx` + rewrite `'/s/:token' → '/s?token=:token'` in `next.config.mjs`. Mirrors `/o`'s pattern exactly. Avoids `[token]` dynamic-segment trap under `output: 'export'`.
- **R2 server-side byte-copy** for `/import` (recipient-side library import). NOT a reference. Preserves invariant that every `files` row's `file_key` starts with that row's `user_id` — keeps stats / purge / delete / download routes working unchanged.
- **`token_hash` (sha256) in DB**, never the raw token. Raw token shown to user once at create.
- **Live `(user_id, book_hash)` resolution** at every access (no FK to `files`). Re-uploads of the same hash follow the share automatically.
- **GET `/download` is a 302 redirect with NO DB writes**. Count via separate `POST /download/confirm` so unfurlers / prefetchers can't inflate counts.
- **Atomic SQL `download_count = download_count + 1`**, not read-modify-write.
- **Per-user 50-share cap**, enforced at create-time.
- **Auth detection on /s landing**: server-render via Supabase auth cookie in `app/s/layout.tsx`. No layout shift. Falls back to anonymous flow + post-hydration upgrade if SSR fails.
- **Cover-less `og.png` fallback**: text-only display-type card. NO placeholder rectangle, NO procedural pattern.
- **Universal Links / App Links**: handle `https://web.readest.com/s/...` in v1 via `useOpenShareLink`. Tauri's `applinks` config already covers the host.
## Cherry-picks accepted (CEO review SELECTIVE EXPANSION)
1. **Position-aware share** — every share initiated from the reader auto-includes `cfi`; recipient lands at sharer's paragraph. Toggle in dialog (default on, only visible when reader-context).
3. **1-tap "Add to my library"** — logged-in recipient on `/s` gets primary action that calls `/import` (R2 byte-copy) and navigates to `/reader?ids=...&cfi=...`.
4. **Branded OG image**`/api/share/[token]/og.png` server-renders via `@vercel/og`. Spec includes anti-slop checklist.
Deferred to TODOS.md: QR code on landing, notify-on-download toggle.
## Open implementation questions (resolve mid-build)
1. Upload-confirmation: HEAD R2 in `/create`, or add `uploaded_at` column to `files`? — recommend HEAD.
2. Migration directory: project has no `apps/readest-app/supabase/migrations/`. Confirm where SQL actually lands before writing the file.
3. `@vercel/og` runtime compat with CloudFlare Workers (OpenNextJS). Verify before relying on it; fallback to Satori + sharp if incompatible.
4. App Router route handlers under `src/app/api/share/...` should be silently dropped by `output: 'export'` in Next 16.2.3. Confirm during first Tauri build.
## Critical files for implementation
See "Critical Files (modify or create)" table in the plan. Key starting points:
- `src/libs/storage-server.ts` — extract `getDownloadSignedUrl` from `pages/api/storage/download.ts`
- `src/libs/share-server.ts` (new) — `generateShareToken()`, `hashShareToken(raw)`
- `src/libs/share.ts` (new) — typed client used by dialog, manage section, deeplink hook, landing page
- `src/utils/share.ts` (new) — `buildShareUrl(token)`, `parseShareDeepLink(url)`
- Dialog reference: `src/components/Dialog.tsx`, `BookDetailModal.tsx`
- Landing reference: `src/app/o/page.tsx` (lift `Card`, `BrandHeader`, `PageFooter` to `src/components/landing/`)
- Deeplink hook reference: `src/hooks/useOpenAnnotationLink.ts`, `useOpenWithBooks.ts`
- App-level upload entry: `appService.uploadBook(book)` at `src/services/appService.ts:269` (NOT `cloudService.uploadBook` — lower-level fn)
@@ -0,0 +1,50 @@
# TTS (Text-to-Speech) Fixes Reference
## Architecture
### Key Components
- `TTSController` (`src/services/tts/TTSController.ts`) - Core state machine
- `EdgeTTSClient` (`src/services/tts/EdgeTTSClient.ts`) - Edge TTS provider
- `useTTSControl` hook (`src/app/reader/hooks/useTTSControl.ts`) - React integration
- `useTTSMediaSession` hook (`src/app/reader/hooks/useTTSMediaSession.ts`) - Media controls
### Section-Aware TTS Model
TTS tracks its own section independently from the view via `#ttsSectionIndex`:
- `#initTTSForSection()` - Creates TTS document for a section without changing the view
- `#initTTSForNextSection()` / `#initTTSForPrevSection()` - Navigate TTS across sections
- `#getHighlighter()` - Only returns highlighter if view section matches TTS section
- `onSectionChange` callback - Notifies UI when TTS crosses section boundary
- Highlights use CFI strings (not raw Range objects) for cross-section compatibility
### State Management Pitfalls
1. **`#ttsSectionIndex` must match view section for highlights to work**
- If `-1`, all highlight calls are suppressed
- `shutdown()` sets it to `-1` but must also null out `this.view.tts`
2. **Guards/Refs that block re-entry:**
- The old `ttsOnRef` guard blocked TTS restart from annotations (removed in #3292)
- `view.tts` reference surviving shutdown blocked re-initialization (#3400)
3. **Timeouts that fire after pause:**
- Edge TTS had a safety timeout that advanced sentences even when paused (#3244)
- Solution: removed the entire `ontimeupdate` safety timeout mechanism
## Fix History
| Issue | Problem | Root Cause | Fix |
|-------|---------|------------|-----|
| #3100 | TTS scrolls too far | TTS coupled to view section | Added `#ttsSectionIndex`, "Back to TTS Location" button |
| #3198 | TTS doesn't follow to next section | No `onSectionChange` callback | Added section change notification, extracted hooks |
| #3244 | Paused TTS advances | Safety timeout fires after pause | Removed `ontimeupdate` timeout mechanism |
| #3291 | TTS fails without lang attribute | Invalid SSML from missing lang | Set lang/xml:lang on html element from `ttsLang` |
| #3292 | Can't restart TTS from annotation | `ttsOnRef` blocks re-entry | Removed the guard ref entirely |
| #3400 | TTS highlight stops after restart | `view.tts` not nulled on shutdown | Added `this.view.tts = null` in `shutdown()` |
## Debugging TTS Issues
1. **TTS doesn't start:** Check `#initTTSForSection()` - does `view.tts.doc === doc` shortcut early?
2. **No highlights:** Check `#ttsSectionIndex` matches view's section index
3. **Advances when paused:** Look for setTimeout/timer callbacks that bypass pause state
4. **Can't restart:** Check for refs/guards that prevent re-entry into speak handlers
5. **Fails on some chapters:** Check if chapter has lang attribute and XHTML namespace
6. **SSML errors:** Check `src/utils/ssml.ts` for proper namespace/lang handling
@@ -0,0 +1,92 @@
---
name: Virtuoso + OverlayScrollbars pattern
description: How to integrate OverlayScrollbars with react-virtuoso for overlay scrollbars on Android/iOS webviews
type: reference
originSessionId: 9da59a46-3dff-4a77-b7a4-8de4d07297b6
---
Virtuoso manages its own internal scroller. On Android WebView (and similar) native scrollbars auto-hide, so users see no scrollbar. The fix: wrap Virtuoso with OverlayScrollbars using the `useOverlayScrollbars` hook — **not** the `OverlayScrollbarsComponent`.
## Migration from `customScrollParent`
The previous approach used `customScrollParent` to let an outer `OverlayScrollbarsComponent` own the scroll. This was replaced: Virtuoso now owns its own scroller, and OverlayScrollbars wraps it. This means:
- Remove `customScrollParent` prop from Virtuoso/VirtuosoGrid
- Remove the outer `OverlayScrollbarsComponent` wrapper
- Use `scrollerRef` instead to capture Virtuoso's scroller element
- If the parent needs the scroller ref (e.g. for pull-to-refresh, scroll save/restore), expose it via a callback prop like `onScrollerRef`
## Boilerplate
```tsx
import { useOverlayScrollbars } from 'overlayscrollbars-react';
import 'overlayscrollbars/overlayscrollbars.css';
// Inside the component:
const osRootRef = useRef<HTMLDivElement>(null);
const [scroller, setScroller] = useState<HTMLElement | null>(null);
const [initialize, osInstance] = useOverlayScrollbars({
defer: true,
options: { scrollbars: { autoHide: 'scroll' } },
events: {
initialized(instance) {
const { viewport } = instance.elements();
viewport.style.overflowX = 'var(--os-viewport-overflow-x)';
viewport.style.overflowY = 'var(--os-viewport-overflow-y)';
},
},
});
useEffect(() => {
const root = osRootRef.current;
if (scroller && root) {
initialize({ target: root, elements: { viewport: scroller } });
}
return () => osInstance()?.destroy();
}, [scroller, initialize, osInstance]);
const handleScrollerRef = useCallback((el: HTMLElement | Window | null) => {
const div = el instanceof HTMLElement ? el : null;
setScroller(div);
// If parent needs the scroller (e.g. for pull-to-refresh):
onScrollerRef?.(div as HTMLDivElement | null);
}, [onScrollerRef]);
```
## JSX structure
```tsx
<div ref={osRootRef} data-overlayscrollbars-initialize='' className='h-full'>
<Virtuoso
scrollerRef={handleScrollerRef}
style={{ height: containerHeight }}
totalCount={items.length}
itemContent={renderItem}
overscan={200}
/>
</div>
```
For `VirtuosoGrid`, same pattern — pass `scrollerRef={handleScrollerRef}`.
## Footer spacer
When Virtuoso owns its own scroller (no `customScrollParent`), the last items may be hidden behind bottom UI (tab bars, safe area). Add a Virtuoso `Footer` component to the components config:
```tsx
const VIRTUOSO_COMPONENTS = {
List: MyListComponent,
Footer: () => <div style={{ height: 34 }} />,
};
```
## Key points
- **`useOverlayScrollbars`** hook, not `OverlayScrollbarsComponent` — the component can't share a viewport with Virtuoso
- Wrapper div needs `ref={osRootRef}` and `data-overlayscrollbars-initialize=""`
- `initialize({ target: root, elements: { viewport: scroller } })` tells OverlayScrollbars to use Virtuoso's existing scroller as its viewport (no new DOM element)
- The `initialized` event **must** restore overflow CSS vars (`--os-viewport-overflow-x/y`) so OverlayScrollbars doesn't fight Virtuoso's scroll management
- No custom Scroller component needed — `scrollerRef` replaces the old `Scroller` component pattern (e.g. `TOCScroller` was removed)
## Used in
- `src/app/library/components/Bookshelf.tsx` — library grid/list with parent scroller exposure for pull-to-refresh and scroll save/restore
- `src/app/reader/components/sidebar/TOCView.tsx` — sidebar TOC (self-contained, no parent scroller needed)
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,5 @@
## Test-First Development
- Always write a failing unit test **before** implementing a fix.
- Run the test to confirm it reproduces the bug or fails as expected, then apply the fix and verify the test passes.
- Run the full test suite (`pnpm test`) after changes to ensure no regressions.
@@ -0,0 +1,5 @@
## TypeScript
- Never use the `any` type. Use `unknown`, proper types, or generics instead.
- Strict mode is enabled. Target is ES2022.
- Unused vars prefixed with `_` are allowed (ESLint configured).
@@ -0,0 +1,9 @@
## Verification (done-conditions)
Before marking work complete, all applicable checks must pass:
1. `pnpm test` — unit tests (vitest)
2. `pnpm lint` — Biome + tsgo (also runs `pnpm lint:lua` if luajit is installed)
3. `pnpm test:lua` — busted unit tests for `apps/readest.koplugin/spec/` (only when koplugin Lua files changed; soft-skips when busted/luajit not installed)
4. `pnpm fmt:check` — Rust format check (only when `src-tauri/` files changed)
5. `pnpm clippy:check` — Rust lint (only when `src-tauri/` files changed)
@@ -0,0 +1,111 @@
---
name: i18n-koplugin
description: >
Extract i18n strings from readest.koplugin Lua sources and translate empty
msgstrs in apps/readest.koplugin/locales. Use when the user invokes
/i18n-koplugin or asks to extract/translate koplugin i18n strings.
Runs scripts/extract-i18n.js to sync .po catalogs from `_("...")` calls,
then fills any empty `msgstr ""` entries across all locale files.
user_invocable: true
---
Extract/translate i18n strings for `readest.koplugin`. The catalogs are gettext `.po` files (not JSON like the main app). Run from the repo root or any worktree — the script resolves paths relative to the plugin dir.
## Step 1: Determine the working directory
If currently in a PR worktree (e.g., `/Users/chrox/dev/readest-pr-*`), use that. Otherwise use the main repo. The plugin dir is `<repo-root>/apps/readest.koplugin`.
## Step 2: Extract msgids from Lua sources
```bash
cd <repo-root>/apps/readest.koplugin
node scripts/extract-i18n.js
```
This scans every `*.lua` file under `apps/readest.koplugin/` (except `spec/` and dotdirs) for `_("...")` and `_([[...]])` calls, then for each language listed in `apps/readest-app/i18next-scanner.config.cjs`:
- appends new msgids with empty `msgstr ""`
- preserves existing translations
- drops obsolete msgids
- rewrites the `.po` header (Plural-Forms etc.)
The output prints `<lang> <kept>/<total> (-<dropped> obsolete)` per locale.
## Step 3: Find untranslated entries
An untranslated entry is a non-empty `msgid` followed by an empty `msgstr ""` (the file's header pair `msgid ""` / `msgstr ""` is NOT a translation — skip it).
```bash
cd <repo-root>/apps/readest.koplugin/locales
# List locales that still have untranslated strings, with counts
for f in */translation.po; do
# Count empty msgstrs that follow a non-empty msgid
n=$(awk '
/^msgid "/ { msgid=$0; next }
/^msgstr ""$/ { if (msgid != "msgid \"\"") c++; next }
' "$f")
[ "$n" -gt 0 ] && echo "$f: $n untranslated"
done
```
If no results, report that all strings are translated and stop.
To list the actual untranslated msgids in one locale:
```bash
awk '
/^msgid "/ { msgid=$0; next }
/^msgstr ""$/ { if (msgid != "msgid \"\"") print msgid; next }
' <repo-root>/apps/readest.koplugin/locales/<lang>/translation.po
```
## Step 4: Translate empty msgstrs
For each empty `msgstr ""` found:
1. Read the preceding `msgid "..."` — that's the English source string.
2. Identify the target locale from the file path (e.g., `locales/ja/translation.po` → Japanese; see table below).
3. Provide an accurate translation. Use the locale reference table for the language; match the tone/terminology already used in the same file (check existing translated entries for context).
4. Preserve `printf`-style placeholders verbatim: `%s`, `%d`, `%1$s`, `%(name)s`, etc.
5. Preserve newlines as `\n`, tabs as `\t`, and escape `"` as `\"` and backslashes as `\\` inside the msgstr.
Edit the `.po` files directly with the Edit tool — do NOT use sed for this, because msgids may contain characters that confuse shell quoting. Each replacement should target the unique `msgid "<English>"\nmsgstr ""` block:
Old:
```
msgid "<English string>"
msgstr ""
```
New:
```
msgid "<English string>"
msgstr "<translation>"
```
Batch all locales for the same key together when possible — keeps the translation set consistent.
### Locale reference
The supported language set is **not hardcoded in this skill**. The ground truth is `apps/readest-app/i18n-langs.json` — both `i18next-scanner.config.cjs` (via `require`) and `src/i18n/i18n.ts` (via JSON import) source from it, and `extract-i18n.js` reads `lngs` from the scanner config at runtime. To list the current set:
```bash
cat <repo-root>/apps/readest-app/i18n-langs.json
```
Map each code to a language name when translating. If a code in `i18n-langs.json` is missing from the `LANG_META` table inside `extract-i18n.js`, the script prints `<code> skipped (no metadata in extract-i18n.js)` — in that case, add the metadata entry there first, then re-run extraction.
## Step 5: Verify
Re-run the count loop from Step 3 and confirm zero untranslated strings remain. Report:
- number of msgids extracted
- per-locale count of strings translated
- any locales that were already complete
Optionally, run the koplugin Lua tests if `busted`/`luajit` are installed:
```bash
cd <repo-root>/apps/readest-app
pnpm test:lua
```
@@ -0,0 +1,96 @@
---
name: i18n
description: >
Extract i18n strings, translate missing translations, or add a new language to readest-app.
Use when the user invokes /i18n or asks to extract/translate i18n strings or add a new locale.
Runs i18next-scanner to extract keys, then translates any __STRING_NOT_TRANSLATED__
placeholders across all locale files.
user_invocable: true
---
Extract/translate i18n strings or add a new language for readest-app. Run from the app directory (either the main repo or a worktree).
## Step 0: Determine the mode
- If the user asks to **add a new language/locale**, go to **Adding a New Language** below.
- Otherwise, go to **Extracting & Translating Strings** below.
## Step 1: Determine the working directory
If currently in a PR worktree (e.g., `/Users/chrox/dev/readest-pr-*`), use that. Otherwise use the main repo. The app directory is `<repo-root>/apps/readest-app`.
---
## Adding a New Language
When the user asks to add a new language (e.g., "add Hungarian", "add hu locale"):
### Step A1: Register the locale in two places
1. **`i18n-langs.json`** — Append the locale code to the array. Both `i18next-scanner.config.cjs` and `src/i18n/i18n.ts` import from this file, so they pick up the new entry automatically.
2. **`src/services/constants.ts`** — Add an entry to `TRANSLATED_LANGS` with the locale code and native language name (e.g., `hu: 'Magyar'`). If the locale already exists in `TRANSLATOR_LANGS`, remove the duplicate there since it will be inherited via the spread.
### Step A2: Generate the translation file
```bash
cd <app-dir>
pnpm run i18n:extract
```
This creates `public/locales/<code>/translation.json` with all keys set to `__STRING_NOT_TRANSLATED__`.
### Step A3: Translate all strings
Follow **Step 4** below to translate every `__STRING_NOT_TRANSLATED__` entry in the new locale file.
### Step A4: Verify
Follow **Step 5** below to confirm zero remaining untranslated strings for the new locale.
---
## Extracting & Translating Strings
### Step 2: Extract i18n strings
```bash
cd <app-dir>
pnpm run i18n:extract
```
This runs `i18next-scanner` which scans source files for translation keys and adds any new keys to all locale files with `__STRING_NOT_TRANSLATED__` as the placeholder value.
### Step 3: Find untranslated strings
```bash
grep -r "__STRING_NOT_TRANSLATED__" <app-dir>/public/locales/
```
If no results, report that all strings are already translated and stop.
### Step 4: Translate missing strings
For each `__STRING_NOT_TRANSLATED__` found:
1. Identify the English key (e.g., `"Hide Scrollbar"`)
2. Identify the target locale from the file path (e.g., `locales/ja/translation.json` -> Japanese)
3. Provide an accurate translation for each locale
Use `sed -i ''` on macOS to replace in-place. Handle all locales in one batch:
```bash
cd <app-dir>/public/locales
sed -i '' 's/"<Key>": "__STRING_NOT_TRANSLATED__"/"<Key>": "<translation>"/' <locale>/translation.json
```
### Locale reference
The canonical, complete list of supported locales lives in `i18n-langs.json` (codes only) and `TRANSLATED_LANGS` in `src/services/constants.ts` (codes → native display names). Read those for the source of truth — translate every locale that appears in `i18n-langs.json`. Don't carry forward older "out-of-scope" exclusions like `pt-BR` or `uz`; if it's in `i18n-langs.json` it ships, and it needs translation.
### Step 5: Verify
```bash
grep -r "__STRING_NOT_TRANSLATED__" <app-dir>/public/locales/
```
Confirm zero remaining untranslated strings. Report the number of keys translated and locales updated.
+1 -1
View File
@@ -3,7 +3,7 @@ PDFJS_FONTS_PATH=../../packages/foliate-js/node_modules/pdfjs-dist
PDFJS_STYLE_PATH=../../packages/foliate-js/vendor/pdfjs
NEXT_PUBLIC_DEFAULT_POSTHOG_URL_BASE64="aHR0cHM6Ly91cy5pLnBvc3Rob2cuY29t"
NEXT_PUBLIC_DEFAULT_POSTHOG_KEY_BASE64="cGhjX2ViNXowbVRxWm8yZm5YYnZGNmE3bFh5TThpTmRSNTNsR1A3VFM3VGh4S08="
NEXT_PUBLIC_DEFAULT_POSTHOG_KEY_BASE64="cGhjX0x3ekhZRWtsZUVub3ZSc05ZQlRpTVRTV2MyS1NUOFdZMzBIWWFhN0ZPa1IK"
NEXT_PUBLIC_DEFAULT_SUPABASE_URL_BASE64="aHR0cHM6Ly9yZWFkZXN0LnN1cGFiYXNlLmNv"
NEXT_PUBLIC_DEFAULT_SUPABASE_KEY_BASE64="ZXlKaGJHY2lPaUpJVXpJMU5pSXNJblI1Y0NJNklrcFhWQ0o5LmV5SnBjM01pT2lKemRYQmhZbUZ6WlNJc0luSmxaaUk2SW5aaWMzbDRablZ6YW1weFpIaHJhbkZzZVhOaklpd2ljbTlzWlNJNkltRnViMjRpTENKcFlYUWlPakUzTXpReE1qTTJOekVzSW1WNGNDSTZNakEwT1RZNU9UWTNNWDAuM1U1VXFhb3VfMVNnclZlMWVvOXJBcGMwdUtqcWhwUWRVWGh2d1VIbVVmZw=="
+2
View File
@@ -28,3 +28,5 @@ S3_ACCESS_KEY_ID=PLACE_HOLDER
S3_SECRET_ACCESS_KEY=PLACE_HOLDER
S3_BUCKET_NAME=PLACE_HOLDER
S3_REGION=PLACE_HOLDER
TEMP_STORAGE_PUBLIC_BUCKET_NAME=PLACE_HOLDER
+3 -1
View File
@@ -1 +1,3 @@
NEXT_PUBLIC_APP_PLATFORM=tauri
NEXT_PUBLIC_APP_PLATFORM=tauri
DBUS_ID=com.bilingify.readest
+2
View File
@@ -0,0 +1,2 @@
NEXT_PUBLIC_APP_PLATFORM=tauri
AI_GATEWAY_API_KEY=your_key_here
+3
View File
@@ -0,0 +1,3 @@
NEXT_PUBLIC_APP_PLATFORM=web
AI_GATEWAY_API_KEY=your_key_here
NEXT_PUBLIC_AI_GATEWAY_API_KEY=your_key_here
+11
View File
@@ -8,6 +8,8 @@
# testing
/coverage
.test-sandbox-node/
.vitest-attachments/
# next.js
/.next/
@@ -48,6 +50,9 @@ tauri.*.conf.json
*.tsbuildinfo
next-env.d.ts
# eslint
.eslintcache
#generated
src-tauri/gen
@@ -60,3 +65,9 @@ src-tauri/gen
/public/fallback-*.js
/public/swe-worker-*.js
/dist/
.context/
.claude/settings.local.json
.claude/skills
+111
View File
@@ -0,0 +1,111 @@
## Project Overview
Readest is a cross-platform ebook reader built as a **Next.js 16 + Tauri v2** hybrid app. It's part of a pnpm monorepo at `/apps/readest-app/`. The app runs on web (CloudFlare Workers), desktop (macOS/Windows/Linux via Tauri), and mobile (iOS/Android via Tauri).
## Common Commands
```bash
# Development
pnpm dev-web # Web-only dev server (no Rust compilation needed)
pnpm tauri dev # Desktop dev with Tauri (compiles Rust backend)
# Building
pnpm build # Build Next.js for Tauri
pnpm build-web # Build Next.js for web deployment
# Testing (see [docs/testing.md](docs/testing.md) for full details)
pnpm test # Unit tests (vitest + jsdom)
pnpm test -- src/__tests__/utils/misc.test.ts # Run a single test file
pnpm test -- --watch # Watch mode
pnpm test:browser # Browser tests (Chromium via Playwright)
pnpm tauri:dev:test # Start Tauri app with webdriver
pnpm test:tauri # Run Tauri integration tests
# Linting & Formatting
pnpm lint # Biome (linter) + tsgo (type check)
pnpm format # Prettier (runs from monorepo root)
pnpm format:check # Check formatting without writing
# Rust
pnpm fmt:check # Check formatting Rust code (src-tauri)
pnpm clippy:check # Lint Rust code (src-tauri)
```
### Source Layout
| Directory | Purpose |
| ----------------- | ------------------------------------------------------------- |
| `src/app/` | Next.js App Router pages and API routes |
| `src/components/` | React components (reader, settings, library, assistant, etc.) |
| `src/services/` | Business logic: TTS, translators, OPDS, sync, AI, metadata |
| `src/store/` | Zustand state stores |
| `src/hooks/` | Custom React hooks |
| `src/libs/` | Document loaders, payment, storage, sync |
| `src/utils/` | Pure utility functions |
| `src/types/` | TypeScript type definitions |
| `src/context/` | React Context providers (Auth, Env, Sync, etc.) |
| `src/workers/` | Web Workers for background tasks |
| `src-tauri/` | Rust backend: Tauri plugins, platform-specific code |
### Path Aliases (tsconfig)
- `@/*``./src/*`
- `@/components/ui/*``./src/components/primitives/*`
### Rust Backend (`src-tauri/`)
Platform-specific code lives in `src-tauri/src/{macos,windows,android,ios}/`. Custom Tauri plugins are in `src-tauri/plugins/`.
## Git Worktrees
Always use `pnpm worktree:new <branch-name|pr-number>` to create worktrees. Never use `git worktree add` directly — the script handles submodule initialization (simplecc WASM, foliate-js), dependency installation, `.env` copying, vendor assets, and Tauri gen symlinks that are required for lint and tests to pass.
```bash
pnpm worktree:new feat/my-feature # New branch from origin/main
pnpm worktree:new 3837 # Checkout PR #3837 with push access to fork
```
## Project Rules
Rules are in `.claude/rules/`: test-first, typescript, verification.
### i18n
See [docs/i18n.md](docs/i18n.md) for the key-as-content translation approach, `stubTranslation` usage in non-React modules, and extraction workflow.
### Safe Area Insets
See [docs/safe-area-insets.md](docs/safe-area-insets.md) for rules on handling top/bottom insets for UI elements near screen edges.
### Design System
UI/UX rules — surface tiers, action vocabulary, settings primitives (`BoxedList`, `SettingsRow`, `SettingsSwitchRow`, `SettingsSelect`, `NavigationRow`, `Tips`, etc.), boxed-list anatomy, RTL conventions, e-ink overlay, and anti-patterns — live in [DESIGN.md](DESIGN.md). Codify recurring decisions there so they persist for the team and future contributors. Reach for the primitives in `src/components/settings/primitives/` instead of inlining chassis classes.
### E-ink mode
Every new UI widget must look right under `[data-eink='true']`. E-ink screens have no shadows, no gradients, slow refresh, and need crisp 1px borders for delineation. The conventions live in `src/styles/globals.css` — reuse the existing classes instead of inventing new ones:
- **Surfaces / inputs** — add `eink-bordered`. In eink mode it swaps to `bg-base-100` + 1px `base-content` border. Use it on inputs, custom button backgrounds, ghost-styled cancel buttons, and any container that needs a visible boundary.
- **Primary action buttons** — add `btn-primary` (alongside whatever Tailwind classes you use for color themes). The `[data-eink] .btn-primary` rule inverts to `base-content` bg + `base-100` text so the primary CTA stays distinct from secondary actions.
- **`.modal-box`** picks up no-shadow + 1px border automatically; dialogs that use it don't need additions.
- **Don't rely on color/shadow alone for hierarchy.** Two same-tone buttons differ only by hover on color themes, and hover doesn't exist on e-ink touchscreens. Pair a borderless ghost (cancel) with a solid CTA (submit) so eink can invert one without flattening the difference.
When in doubt, toggle E-ink in Settings → Misc and check. The rules in `globals.css` cover most cases automatically, but composite components (custom buttons, layered cards) often need `eink-bordered` on the right element to stay legible.
Available gstack skills:
- `/plan-ceo-review` — CEO/founder-mode plan review
- `/plan-eng-review` — Eng manager-mode plan review
- `/plan-design-review` — Designer's eye review of a live site
- `/design-consultation` — Design system consultation
- `/review` — Pre-landing PR review
- `/ship` — Ship workflow (merge, test, review, bump, PR)
- `/browse` — Fast headless browser for QA and site interaction
- `/qa` — QA test and fix bugs
- `/qa-only` — QA report only (no fixes)
- `/qa-design-review` — Designer's eye QA with fixes
- `/setup-browser-cookies` — Import cookies for authenticated testing
- `/retro` — Weekly engineering retrospective
- `/document-release` — Post-ship documentation update
If gstack skills aren't working, run `cd .claude/skills/gstack && ./setup` to build the binary and register skills.
+1
View File
@@ -0,0 +1 @@
AGENTS.md
+972
View File
@@ -0,0 +1,972 @@
## Readest Design Language
Readest's UI is **Adwaita-aligned**, **e-ink-first**, **cross-platform-aware**. This doc is the
reference for that language: principles, vocabulary, anti-patterns. New work should read it
before reaching for daisyui defaults; existing work is gradually migrating toward it.
### Status
This doc is the **first articulation** of the system, not a retrospective. Many existing
components don't fully match it yet (especially older buttons and ad-hoc panels). The goal
is that **new code uses these conventions** and **migrations land opportunistically** as
features get touched.
---
### 1. Identity & lineage
Readest's visual language descends from **Adwaita / libadwaita** — GNOME's design system —
adapted for a cross-platform Tauri + Next.js app that also runs on iOS, Android, web, and
e-ink readers.
What we take from Adwaita:
- **Content first, chrome recedes.** The reading surface is the product. Settings, toolbars,
popups never compete with the page.
- **Boldly minimal.** Restraint over density. Whitespace is structural.
- **Surface hierarchy** — window → view → card — three explicit elevation tiers, no shadow
gymnastics.
- **Color discipline.** Brand color is rare and earned. Neutral palette carries the weight.
- **Boxed lists are the chassis.** AdwActionRow's prefix · title · suffix anatomy is the
canonical settings/list row everywhere.
- **Pills, ghosts, flats.** Three-tier button palette: pill/circular ghost in headers, flat
secondary over view-bg, accent CTA only when truly primary.
- **Banner vs Toast.** AdwBanner = inline, top-of-window, persistent. AdwToast = transient,
bottom slide-in.
- **Switches over checkboxes** for boolean settings.
- **Subtle motion.** Short, ease-out, never bouncy.
What's Readest-specific:
- **E-ink as a first-class mode.** Every surface flips to flat 1px contrast borders under
`[data-eink='true']`. Adwaita is desktop-GNOME-only; we ship to e-ink readers and the
visual language has to survive there.
- **Cross-platform reality.** Readest runs on macOS, Windows, Linux, iOS, Android, web. The
identity stays Adwaita; platform grace notes (radii, target sizes) follow host
conventions where they matter.
---
### 2. Principles
The seven rules. When in doubt, work backward from these.
#### 2.1 Surfaces continue surfaces
A control that extends a list/card should match its parent's border + fill. The
"+ Import Dictionary" button at `src/components/settings/CustomDictionaries.tsx` reads as
detached card siblings of the dictionary list above it because they share
`border-base-200 bg-base-100 rounded-lg`.
> **Bad**: a list of dictionaries in a `bg-base-100` card, followed by a `btn-outline btn-primary`
> add button. The button shouts; the list whispers; the eye bounces.
>
> **Good**: list and add-button share the same surface vocabulary. The eye flows.
#### 2.2 Color is earned
Brand `primary` is reserved for **the** primary action of a surface. Most actions don't have
a primary action — they have a list of equally-weighted choices, or a single accent.
- Settings dialog has no primary. Every panel is a list of toggles. **Zero brand color.**
- "Import a Book" in onboarding is a primary CTA. **One brand color.**
- "Add Web Search" extends a list — it's not the surface's primary action. **Neutral.**
#### 2.3 Two-step depth
State changes cycle through **`base-100 → base-200 → base-300`** instead of recoloring.
Hover lifts, active deepens, disabled fades opacity. This is theme-safe (works across all
11 color themes), e-ink-friendly (depth is preserved as borders, not shades), and
calmer than recoloring.
#### 2.4 Localize the hover signal
When a button hovers, **one focal element changes**, not the whole button. The icon chip
inverts; the label stays steady. The badge intensifies; the row stays neutral. This reads
as deliberate, not decorative.
#### 2.5 Motion is color, not transform
Default to `transition-colors duration-150`. No `scale`, no `translate`, no `rotate` unless
the motion **is** the message (a chevron rotating to indicate expansion is fine; a button
that scales on hover is not). Transforms break under `[data-eink='true']` and feel
gimmicky under Adwaita's calm rhythm.
#### 2.6 Eink-first by default
Every custom-styled bordered surface gets the `eink-bordered` class. Every primary action
gets `btn-primary` (which has dedicated eink rules). Don't rely on color or shadow alone
for hierarchy — eink screens have neither.
If you can't toggle Settings → Misc → Eink and still tell which button is the CTA, the
hierarchy is broken.
#### 2.7 Focus is visible but quiet
Keyboard focus needs a visible ring. `focus-visible:ring-2 focus-visible:ring-base-content/15`
is the canonical treatment for custom buttons. Loud `ring-primary` reserved for inputs
where the focus state IS the affordance.
#### 2.8 RTL: always use logical properties (REQUIRED)
Readest ships with RTL languages enabled. **Never use direction-bound Tailwind
utilities** when a logical equivalent exists — the visual edges flip in RTL,
the logical ones don't.
| Don't use | Use instead |
| ---------------------------------- | ---------------------------------- |
| `pl-*` / `pr-*` | `ps-*` (start) / `pe-*` (end) |
| `ml-*` / `mr-*` | `ms-*` / `me-*` |
| `text-left` / `text-right` | `text-start` / `text-end` |
| `border-l` / `border-r` | `border-s` / `border-e` |
| `rounded-l-*` / `rounded-r-*` | `rounded-s-*` / `rounded-e-*` |
| `left-*` / `right-*` (positioning) | `start-*` / `end-*` |
| `justify-start` / `justify-end` | (these ARE direction-aware) — keep |
The `flex-row` direction is automatically reversed in RTL by the browser, so
you usually don't need to do anything for `flex` / `gap`. Only **explicit
edges** (padding, margin, borders, radius, absolute positioning) need
logical properties.
**Quick scan when reviewing a diff:** grep for `\b(pl|pr|ml|mr|left-|right-|text-left|text-right|border-l|border-r|rounded-l|rounded-r)-` in changed files. Any hit that isn't a deliberate LTR-only
case (rare — usually only icon glyphs that have a fixed orientation) should
be flipped to the logical equivalent.
#### 2.9 Every panel and sub-page starts with title + description (REQUIRED)
Every settings panel and every sub-page must open with:
1. **A title** — the panel name. Style: `text-lg font-semibold tracking-tight`. In a
top-level panel this is an `<h2>`; in a sub-page this is the `parentLabel /
currentLabel` breadcrumb in `SubPageHeader` (which uses the same typography so the
word stays anchored visually as the user navigates in/out).
2. **A one-line description** — a short sentence under the title explaining what this
surface does or how it fits in the user's workflow. Style: `text-sm
text-base-content/70 leading-relaxed`. Skip it only when the surface is so trivial
the breadcrumb already says everything (rare — when in doubt, write one).
Why: orientation, visual rhythm, and Adwaita parity (`AdwPreferencesPage` always has
both). The same vertical opening across every surface makes the system feel cohesive
and gives users a predictable place to learn what a screen does.
**Canonical components.** The `<SubPageHeader>` primitive in
`src/components/settings/SubPageHeader.tsx` accepts a `description?: React.ReactNode`
prop that renders the description in the canonical style — sub-pages should pass it
there rather than rolling their own `<p>` below the header. Top-level panels currently
inline the title + description; if a third or fourth panel needs the same pattern,
extract a `<PanelHeader>` primitive following the same shape.
**Examples.**
```tsx
// Sub-page (Integrations → OPDS Catalogs)
<SubPageHeader
parentLabel={_('Integrations')}
currentLabel={_('OPDS Catalogs')}
description={_('Browse and download books from online catalogs')}
onBack={() => setSubPage(null)}
/>
// Top-level panel (Integrations panel root)
<div className='w-full'>
<h2 className='mb-1.5 text-lg font-semibold tracking-tight'>{_('Integrations')}</h2>
<p className='text-base-content/70 text-sm leading-relaxed'>
{_('Connect Readest to external services for sync, highlights, and catalogs.')}
</p>
</div>
```
---
### 3. Surface hierarchy
Three named tiers, mapped onto daisyui tokens. Use these terms in conversation and code
comments even though the classes are still daisyui-native.
| Tier | Token | Role | Example |
| ---------- | ------------------------------------ | ----------------------------------------------------------------------------- | ----------------------------------------------- |
| **Window** | `bg-base-200` | The outermost backdrop. Modal scrims, dialog content area, scroll containers. | `<Dialog>` body |
| **View** | `bg-base-100/60` or `bg-base-200/40` | Mid-tier surface inside a window. Tip boxes, secondary panels. | The "提示 / Tips" callout in CustomDictionaries |
| **Card** | `bg-base-100` | Top-tier content surface. Boxed lists, popovers, modal-box. | The dictionaries list card |
Border treatment:
- **Window** has no border (it IS the boundary).
- **View** uses no border or `border-base-200/60` for very soft delineation.
- **Card** uses `border border-base-200`. In e-ink, `eink-bordered` flips it to 1px
`border-base-content`.
Corner radius:
- **Card / View**: `rounded-lg` (8px) — Readest's house radius. Adwaita uses 9px; 8px is
close enough and matches Tailwind's scale.
- **Modal / Sheet**: `modal-box` default (~1rem / 16px) — bigger surfaces get bigger radii.
- **Pills / Chips**: `rounded-full`.
- **Inputs / small buttons**: `rounded-md` (6px) or `rounded-lg` (8px).
#### Surface continuity rule
When a control extends a card (an "add row" affordance, a footer button bar attached to a
list), it inherits the card's surface treatment: same `bg-base-100`, same
`border-base-200`, same `rounded-lg`. It is the card grown by one row.
---
### 4. Action vocabulary
Six archetypes. Pick by **role**, not by **appearance**.
#### 4.1 Accent CTA
The primary, accent-colored button. **One per surface, max.** Submit on a form, "Open
Book", "Sign In".
```tsx
className = 'btn btn-primary';
```
Eink: `btn-primary` has dedicated rules (inverts to base-content bg + base-100 text) so it
stays distinct from secondary actions on monochrome screens.
#### 4.2 Suggested
A non-accent-but-emphasized action. Used when there are multiple equally-weighted actions
and one is the recommended path. Adwaita's "suggested-action" CSS class.
```tsx
className = 'btn btn-neutral';
```
Rare. Most surfaces don't need this tier.
#### 4.3 Flat
The default secondary button. Sits on a view or card surface, no border, hover lifts to
`base-200`. The bulk of buttons should be flat.
```tsx
className="btn btn-ghost"
// or for a custom surface treatment:
className={clsx(
'rounded-lg px-4 py-2 text-sm font-medium',
'hover:bg-base-200 transition-colors duration-150',
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-base-content/15',
)}
```
#### 4.4 Pill / Circular ghost
Compact icon-only buttons in header bars and toolbars. Always `rounded-full`,
`btn-circle` or hand-rolled circular ghost.
```tsx
className = 'btn btn-ghost btn-circle h-8 min-h-8 w-8 p-0';
```
The window controls in `SettingsDialog.tsx` (search, menu, close) use this archetype.
#### 4.5 Destructive
Delete, remove, irreversible. Adwaita uses `destructive-action`. Readest uses red
sparingly — usually only the icon, not the whole button.
```tsx
// Icon-only delete X in delete mode:
className = 'btn btn-ghost btn-sm shrink-0 px-1';
// with <IoMdCloseCircleOutline className="text-error h-4 w-4" />
```
For destructive **dialogs** (confirmation modals), the confirm button can be `btn-error`,
but only in the modal — never on the main surface.
#### 4.6 ListExtension
A Readest-named archetype for "add another row to the list above" affordances. The two
buttons at the bottom of `CustomDictionaries.tsx` are the canonical example.
Anatomy:
- Surface matches the parent card (`border border-base-200 bg-base-100 rounded-lg`)
- Height ~h-11
- Centered: small icon chip + label
- Icon chip: `bg-base-200 text-base-content/60 rounded-full h-5 w-5`
- Hover: border deepens to `base-300`, bg lightens to `bg-base-200/60`, icon chip inverts
to `bg-base-content text-base-100`
- `eink-bordered` on the button itself
```tsx
<button
type='button'
onClick={handleAdd}
className={clsx(
'eink-bordered group flex h-11 items-center justify-center gap-2.5',
'border-base-200 bg-base-100 rounded-lg border px-4',
'text-base-content text-sm font-medium',
'transition-colors duration-150',
'hover:border-base-300 hover:bg-base-200/60',
'active:bg-base-200/80',
'focus-visible:ring-base-content/15 focus-visible:outline-none focus-visible:ring-2',
)}
>
<span
className={clsx(
'flex h-5 w-5 items-center justify-center rounded-full',
'bg-base-200 text-base-content/60',
'transition-colors duration-150',
'group-hover:bg-base-content group-hover:text-base-100',
)}
>
<MdAdd className='h-3.5 w-3.5' />
</span>
<span className='line-clamp-1'>{label}</span>
</button>
```
Use this for: "Import Dictionary", "Add Web Search", "Add Custom Theme", any "+ add new
to this list" pattern. **Do not** use `btn-outline btn-primary` for these.
---
### 5. Boxed list anatomy
The settings UI is built on boxed lists. One pattern, used everywhere.
#### Container
Use the `<BoxedList>` primitive at `src/components/settings/primitives/BoxedList.tsx`
rather than inlining the chassis classes:
```tsx
<BoxedList title={_('Reading Sync')} data-setting-id='settings.section.id'>
{/* rows */}
</BoxedList>
```
The primitive renders:
```tsx
<div className='card eink-bordered border-base-200 bg-base-100 border'>
<div className='divide-base-200 divide-y'>{children}</div>
</div>
```
- `card` for the radius
- `border border-base-200` for the boundary (eink upgrades this automatically)
- `eink-bordered` for the e-ink-mode contrast border
- `divide-base-200 divide-y` for inter-row separators
> **No `overflow-hidden` on the card.** Children may host popovers (color
> pickers, dropdowns, tooltips) that need to escape the card bounds. The
> `divide-y` rules sit between rows and don't touch the card's rounded
> corners, so omitting overflow-clip is visually safe AND keeps embedded
> popovers from getting clipped.
#### Row anatomy
Three slots, in order, always:
```
┌─────────────────────────────────────────────────────────────────┐
│ [prefix] Title text [suffix slots] │
│ [ ] Subtitle text (optional) [ ][ ]│
└─────────────────────────────────────────────────────────────────┘
```
| Slot | Contents |
| ------------ | ------------------------------------------------------------------------------------------------- |
| **Prefix** | Drag handle, leading icon, avatar, status dot, or empty. |
| **Title** | Primary label. `font-medium`. Truncates with `truncate`. |
| **Subtitle** | Optional secondary line. `text-sm text-base-content/70`. Used for warnings, descriptions, status. |
| **Suffix** | Badge, switch, button, chevron, value, or any combination. End-aligned. |
Canonical example: `SortableRow` in `src/components/settings/CustomDictionaries.tsx`. The
drag handle is the prefix, the dict name is the title, the warning reason is the
subtitle, and the badge + toggle + edit/delete buttons stack as suffixes.
#### Row variants
- **ActionRow** — title + suffix is a single button or chevron. Tap anywhere navigates.
- **SwitchRow** — title + suffix is a toggle. Tap anywhere toggles.
- **ComboRow** — title + suffix is a dropdown/select.
- **ExpanderRow** — chevron suffix; tap expands to reveal nested rows.
These names come from libadwaita and apply 1:1 to Readest's lists. Use the names in code
comments and PR descriptions.
#### Spacing
- Row vertical padding: `py-2` (8px) for compact lists, `py-3` (12px) for breathing room.
- Row horizontal padding: `px-3` (12px) or `px-4` (16px). Stay consistent within a list.
- Slot gap: `gap-2` (8px) between prefix/title/suffix elements.
#### Disabled rows
Disabled rows fade the title to `text-base-content/60` and disable the suffix control. The
row itself stays at full opacity — only the **content** dims, not the row.
#### Toggle size
| Daisyui class | Use case |
| ------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| `toggle` (default, h-5 / ~20px) | **Settings panel boxed-list rows**`<SettingsSwitchRow>` uses this. Visible weight matches the 56px `min-h-14` row. |
| `toggle-sm` (h-4 / ~16px) | Inline secondary switches in tighter contexts — e.g. dictionary list rows in `CustomDictionaries`. |
| `toggle-xs` (h-3 / ~12px) | Compact metadata toggles inside cards — e.g. OPDS catalog "Auto-download". |
The `<SettingsSwitchRow>` primitive bakes in the default `toggle`. **Don't override
to `toggle-sm` inside boxed-list rows** — it looks orphaned in the row's vertical
breathing room. Use the smaller sizes only when the row itself is shorter than 56px.
#### Typography inherits from `.settings-content`
The Settings dialog (and any settings-style sheet/popup) wraps its content
in `.settings-content`, which is defined in `src/styles/globals.css` as:
```css
.dropdown-content,
.settings-content {
font-size: 14px; /* desktop */
}
@media (max-width: 768px) {
.dropdown-content,
.settings-content {
font-size: 16px; /* mobile bump — high-DPI phones need bigger body text */
}
}
```
**Don't hardcode `text-sm` on row labels, NavigationRow titles, or panel
descriptions** — that locks the text to 14px on every viewport and kills
the mobile bump. Instead:
- **Primary labels** (SettingsRow label, NavigationRow title, SubPageHeader
description, ad-hoc row labels in panels and integration forms): no
font-size class — inherits 14/16 from the wrapper. Use `<SettingLabel>`
rather than inlining a `<span>`; it adds `font-medium` for cased scripts
and drops the weight for caseless scripts (CJK / Arabic / Hebrew / Indic
/ Thai / Tibetan), since those bold poorly at body size and `font-medium`
on Han / Hangul / Devanagari renders as uneven stroke-thickening across
system fonts.
- **Secondary text** (SettingsRow description, NavigationRow status, Tips
body, BoxedList description): use `text-[0.85em]` so it stays
proportional (≈12px desktop, ≈13.6px mobile).
- **Form controls** (`<input>`, `<select>`): browsers don't inherit
font-size onto form elements, so add the `settings-content` class
_directly on the element_ to re-apply the 14/16 cascade. The legacy
NumberInput already does this — match its pattern.
- **Section headers** (`BoxedList` uppercase title): use `text-[0.85em]
font-semibold uppercase tracking-wider`. The em-relative size keeps it
proportional with the `.settings-content` cascade. **Caseless-script
exception:** when `isCaselessUILang()` is true, bump to `text-[1em]`.
The `uppercase` rule is a no-op in scripts without case (CJK, Arabic,
Hebrew, Devanagari/Bengali/Tamil/Sinhala, Thai, Tibetan), so the size
has to carry the emphasis those scripts can't pick up from casing. The
helper lives in `src/utils/misc.ts`; the underlying `isCaselessLang`
predicate lists every covered language code in `src/utils/lang.ts`.
Why this matters: Tailwind's `text-xs` / `text-sm` are rem-based — they
ignore the parent's `font-size` because rem is rooted at the document.
The `.settings-content` cascade is in `px`, so any child that picks a
Tailwind size literally tunes itself to the desktop default and never
grows on mobile. iOS and Android have small physical screens but high
DPI, so the mobile bump is what makes the text legible at typical reading
distance.
#### Uniform row height
Settings rows in a boxed list MUST all be the same visual height. Use
`min-h-14 items-center` (56px) on each row container — toggle, select, and
input rows then center their controls vertically inside identical boxes.
**Don't use `py-3`** — content-driven padding produces uneven heights
because toggles, selects (`h-9`), and inputs (`h-9`) have different
intrinsic sizes.
```tsx
// ✓ Right — no text-sm; label inherits .settings-content (14/16)
<label className='flex min-h-14 items-center justify-between px-4'>
<span className='font-medium'>{_('Sync Enabled')}</span>
<input type='checkbox' className='toggle' ... />
</label>
// ✗ Wrong — toggle row will be 48px, select rows 60px
<label className='flex items-center justify-between px-4 py-3'>...</label>
// ✗ Wrong — text-sm hardcodes 14px even on mobile (kills the bump)
<span className='text-sm font-medium'>{_('Sync Enabled')}</span>
```
#### Controls inside a boxed list have no chrome
When a control sits inside a bordered card, it shouldn't carry its own
border or fill. The card supplies the visual boundary; the control just
sits on the row.
- **Selects:** drop `select-bordered` and `eink-bordered`. Add
`!bg-transparent !bg-none !appearance-none` to suppress daisyui's
background chevron and native arrow. Render a real `<MdArrowDropDown>`
icon at the cell's trailing edge for the affordance — see "End-aligned
values" below.
- **Inputs:** drop `input-bordered` and `eink-bordered`. Add `!bg-transparent`
with `hover:!bg-base-200/60 focus:!bg-base-200/60` so the field still
signals interactability. Use `text-end` and `!pe-0` so the value sits
flush against the row's trailing edge.
- **Toggles:** untouched — they're already chromeless.
This is the iOS Settings / Adwaita PreferencesGroup convention: list
chrome belongs to the container, not its children.
#### End-aligned values + chevron alignment
The selected value of a select/input MUST end-align (`text-end`). The
**visible right edge** of every row's value (toggle, chevron icon, input
text) MUST land at the same X — the row's trailing padding.
The trap: daisyui's select renders its chevron via background-image at
`calc(100% - 1rem) center`, which floats the glyph 16px _inside_ the
select's right edge. So if the toggle in row 1 ends at the row's `pe-4`
edge, the chevron in row 2 ends 16px before that — visibly misaligned.
**Fix:** suppress daisyui's bg-image chevron and render an explicit icon at
the cell's trailing edge. The select's own daisyui focus chrome (outline +
box-shadow + ring) is suppressed; **no focus ring** on controls inside the
boxed list — focus state is signaled by a subtle wrapper bg-shift instead
(hover and focus-within both lift to `bg-base-200/60`). Rings would compete
with the card's own border and double-stack with adjacent rows.
```tsx
<div className='hover:bg-base-200/60 focus-within:bg-base-200/60 flex max-w-[60%] items-center rounded-md'>
<select className='select h-9 min-w-0 cursor-pointer !appearance-none truncate !border-0 !bg-transparent !bg-none !pe-1 !ps-2 text-end text-sm focus:!border-0 focus:!shadow-none focus:!outline-none focus:!ring-0'>
{/* options */}
</select>
<MdArrowDropDown
aria-hidden='true'
className='text-base-content/55 pointer-events-none h-5 w-5 flex-shrink-0'
/>
</div>
```
> **Why so many `!` overrides?** daisyui's `.select` and `.input` apply
> `border-width: 1px` + `border-color` (transparent at rest, `var(--bc)` on
> focus), plus `outline`, `box-shadow`, and `ring` chrome on focus. To make
> the control truly chromeless inside a boxed list, you need to kill all
> four properties. Missing any of them — especially `border-0` — leaves a
> visible focus border leaking through.
The `<MdArrowDropDown>` icon's trailing edge now lives at the same X as the
toggle's trailing edge in adjacent rows, because both are flush with the
row's `pe-4` padding.
For inputs, no wrapper is needed — the input is one element, so put the
hover/focus bg directly on it. Suppress daisyui's own focus chrome the
same way:
```tsx
<input className='input hover:!bg-base-200/60 focus:!bg-base-200/60 h-9 max-w-[60%] rounded-md !border-0 !bg-transparent !pe-0 !ps-2 text-end text-sm focus:!border-0 focus:!shadow-none focus:!outline-none focus:!ring-0' />
```
> **Why no ring here when §2.7 says "focus needs a visible ring"?** §2.7 is
> for standalone custom buttons (Submit, Cancel, ListExtension, etc.). In a
> boxed list, the row already provides strong visual containment via the
> card border + dividers, and stacking a per-control ring inside that
> creates double chrome. The bg-shift IS the focus indicator — keyboard
> users still get clear feedback; the surface stays calm.
---
### 6. Header bars, dialogs, popups, sheets
#### Header bar
The dialog/page header. Adwaita's AdwHeaderBar.
- **4856px tall** (`h-12` to `h-14`).
- **Center-aligned title** in `font-semibold text-base`.
- **Leading slot**: back chevron (mobile) or empty (desktop).
- **Trailing slot**: window controls — search (pill ghost), menu (pill ghost),
close (pill ghost circle with `bg-base-300/65`).
- No bottom border; rely on tab/divider that follows.
`SettingsDialog.tsx`'s mobile header is the canonical example. The desktop header is
slightly different — tabs sit in the same row as window controls, no center title — but
it's the same archetype adapted for screen real estate.
#### Dialog (modal)
```tsx
<Dialog
isOpen={...}
onClose={...}
boxClassName="sm:min-w-[520px] overflow-hidden"
header={<HeaderBar />}
>
{/* content */}
</Dialog>
```
- `modal-box` provides the radius, max-width, and shadow (auto-removed in eink).
- Width ~520px on desktop, full-width on mobile.
- Bottom sheets on mobile via `snapHeight` prop.
- Backdrop: `sm:!bg-black/50` (or `/20` when nested over a darker surface).
#### Popup (popover)
For dictionary lookups, annotation editors, and other anchored overlays. Uses the
`Popup` component with a triangle pointer.
- **Width**: clamp to fit content; ~320420px typical.
- **Surface**: `bg-base-100`, `rounded-lg`, soft shadow (eink removes shadow).
- **Triangle**: pointer toward the anchor; eink has special triangle classes.
- **Padding**: `p-3` to `p-4` for content.
#### Sheet (mobile bottom)
Reserved for mobile contextual menus and full-screen secondary panels. Uses the dialog's
`snapHeight` prop. Adwaita doesn't have a native sheet but Readest's mobile pattern is
the closest analog.
- Always full-width.
- Top corners rounded; bottom corners flat (it's anchored to the bottom).
- Drag handle at top (the small horizontal pill) is mandatory if the sheet supports
swipe-to-dismiss.
---
### 7. Motion + a11y
#### Motion
- Default duration: **150ms** for color transitions.
- Default easing: browser default (`ease`) or `ease-out`. Never `ease-in`.
- Longer transitions (300ms+) only for layout changes (sheet snap, panel slide).
- **Never** use `transform` for hover unless the transform IS the message
(chevron rotation, drag-handle drag visualization). E-ink doesn't render mid-transitions
cleanly and Adwaita's identity is calm.
```tsx
// Good — hover:bg-base-200 with transition-colors
className = 'transition-colors duration-150 hover:bg-base-200';
// Bad — scale on hover
className = 'transition-transform hover:scale-105';
```
Existing exceptions: `.window-button` in globals.css uses `hover:scale-105`. That's
legacy; new code shouldn't follow it.
#### Reduced motion
Reduced-motion preference is honored via the `no-transitions` class
(`globals.css:624`). Layout-changing transitions should respect
`prefers-reduced-motion: reduce` either via this class or `motion-safe:` Tailwind
prefixes.
#### Focus
- Every focusable element must have a visible focus indicator.
- Custom buttons:
`focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-base-content/15`.
- Inputs: rely on daisyui's input focus ring; inputs with custom styling use
`focus:ring-2 focus:ring-primary/40`.
- Don't use `outline-none` without `focus-visible:` replacement.
#### Hit targets
- **Minimum**: 32px (the size of `btn-sm`).
- **Recommended**: 40px (`btn`) on touch surfaces.
- **Mobile**: 44px+ for taps that aren't fail-safe (delete, navigate-away).
- The `touch-target` class in globals.css extends a small visual control's hit area to
44px without changing its rendered size — use it on icon-sized buttons in mobile UIs.
#### Color contrast
- Body text on background: WCAG AA (4.5:1) minimum.
- Large text: WCAG AA Large (3:1) minimum.
- Interactive text on hover state: still passes contrast on the new background.
- Theme palette is generated from `(bg, fg, primary)`; the tinycolor pipeline keeps
contrast within range, but custom themes can break this — Settings → Color flags
low-contrast custom themes.
#### Keyboard
- Tab order matches visual order. If you use `flex-row-reverse` for visual layout,
consider `tabIndex` to fix order.
- Modal focus trap: `<Dialog>` handles this.
- Esc to dismiss: `<Dialog>` and `<Popup>` handle this.
- Arrow keys for grouped controls (radio-like tab strips, sortable lists). dnd-kit's
`KeyboardSensor` is wired for sortable lists.
---
### 8. E-ink overlay (cross-cutting)
E-ink mode is toggled by `[data-eink='true']` on the document. It applies a global
override layer in `src/styles/globals.css:484-622` that:
- Removes all `box-shadow`.
- Forces `text-base-content`, `text-blue-*`, `text-red-*`, `text-neutral-content` to a
single foreground color.
- Inverts `btn-primary` and `btn-outline` to base-content bg + base-100 text.
- Adds 1px contrast borders to `.eink-bordered`, `.modal-box`, `.menu-container`,
`.popup-container`, `.alert`, `.opds-navigation .card`, `.booknote-item`,
`.bookitem-main`.
What this means for new components:
| Surface type | Required class | Why |
| ------------------------------------ | ----------------------- | ------------------------------------------------------------- |
| Custom bordered button or input | `eink-bordered` | Gets the 1px contrast border in eink |
| Primary CTA | `btn-primary` | Picks up the inverted treatment |
| Cancel / secondary action | `btn-ghost` (no border) | Reads as "outlined" only after pairing with the CTA |
| Card / panel using `border-base-200` | `eink-bordered` | Otherwise the soft border vanishes in eink |
| Modal / Popup | (auto) | `modal-box` and `.popup-container` are handled in globals.css |
Verification checklist before shipping a new UI:
- [ ] Toggle Settings → Misc → Eink mode and re-test every screen.
- [ ] Every container that has a soft border (`border-base-200`) still has visible
delineation.
- [ ] Every CTA is distinguishable from its neighbors (cancel, secondary).
- [ ] No hover transforms make the UI feel jumpy.
- [ ] Text is fully opaque (no `text-base-content/60` content; eink can't render the
reduced opacity well).
#### What's NOT compatible with e-ink
- Drop shadows for hierarchy (use borders).
- Color-only state changes (use border weight or fill swap).
- Hover scale / translate (they look broken on slow refresh).
- Animations longer than ~200ms (visible refresh artifacts).
---
### 9. Cross-platform grace notes
Readest ships on **macOS, Windows, Linux, iOS, Android, web**. Adwaita is desktop-GNOME-
native; we adapt where the host OS has strong conventions, but never at the cost of
identity.
#### iOS
- Slightly larger corner radii feel native (`rounded-xl` on dialogs, `rounded-lg` on
cards).
- Safe area insets are mandatory for top + bottom anchored elements (see
`docs/safe-area-insets.md`).
- Avoid Material Design ripple effects.
- Sheet-style modals (bottom-anchored) match iOS conventions and are preferred over
centered dialogs on phone-sized screens.
#### Android
- Material 3 conventions that conflict with Adwaita (FABs, elevation shadows, ripple
inks): **don't** copy them. Readest's identity is Adwaita; the user is reading on
Android, not in Android.
- Touch targets bumped to 48px for primary actions (Material's recommended target).
- Back-gesture-aware UIs: ensure swipe-from-edge doesn't conflict with horizontal swipe
controls.
#### Linux
- Native Adwaita territory. Readest can match host theme for window chrome (Tauri
decorations) but should keep its own internal palette for the reading surface — book
themes (sepia, gruvbox, etc.) are user choices, not OS choices.
#### macOS / Windows
- Window controls (close/minimize/maximize) are platform-native via Tauri.
- Title bar height matches platform convention; internal layout follows Readest's
Adwaita palette.
#### Web
- No safe-area insets needed.
- Keyboard shortcuts are doubled with command-palette discoverability (Cmd/Ctrl+K).
- Browser-native focus rings: respected, augmented with `focus-visible:ring-*`.
#### E-ink readers (Android-based, custom firmware)
- Detected via the eink mode toggle (Settings → Misc).
- All rules in §8 apply.
- This is a **first-class** target, not a fallback.
---
### 10. Anti-patterns
Things that LOOK fine in isolation but break the system. Each one has a real source diff
or commit reference.
#### 10.1 Loud outlined CTAs for non-primary actions
```tsx
// Anti-pattern (was in CustomDictionaries.tsx, fixed Nov 2026):
<button className='btn btn-outline btn-primary gap-2 normal-case [--animation-btn:0s]'>
<MdAdd className='h-5 w-5' />
Import Dictionary
</button>
// Correct: ListExtension archetype (see §4.6)
```
Why it broke: the buttons read as primary CTAs but are list extensions. They competed
with the active settings tab indicator and pulled the eye from the list itself.
#### 10.2 Recoloring the whole button on hover
```tsx
// Anti-pattern:
<button className="text-base-content/70 hover:text-base-content hover:bg-primary/10">
// Correct: keep the label color steady, hover via bg shift on the surface
<button className="text-base-content hover:bg-base-200 transition-colors">
```
Why: principle 2.4 (localize the hover signal). Whole-button color shifts feel decorative.
#### 10.3 Transform-based hover
```tsx
// Anti-pattern:
<button className="hover:scale-105 transition-transform">
// Correct: color/border-based hover
<button className="hover:bg-base-200 hover:border-base-300 transition-colors">
```
Why: breaks under e-ink (§2.5), feels jumpy under Adwaita's calm rhythm.
#### 10.4 Soft borders without `eink-bordered`
```tsx
// Anti-pattern:
<div className="border border-base-200 bg-base-100 rounded-lg p-4">
...
</div>
// Correct:
<div className="eink-bordered border border-base-200 bg-base-100 rounded-lg p-4">
...
</div>
```
Why: in e-ink mode, `base-200` borders disappear into the background. `eink-bordered`
flips the border to `base-content` so the boundary stays visible.
Exception: containers that **don't** need a visible boundary in eink (e.g., a
`bg-base-100` surface that's already against `bg-base-200`) can skip `eink-bordered`.
The class is opt-in for "this surface needs a border to read correctly".
#### 10.5 Reduced-opacity text in e-ink
```tsx
// Anti-pattern (in eink):
<span className="text-base-content/50">Optional metadata</span>
// Correct (still readable in eink):
<span className="text-base-content text-xs">Optional metadata</span>
// Or use semantic muting that the eink overlay handles:
<span className="text-neutral-content">Optional metadata</span>
```
Why: e-ink's reduced color depth turns `/50` opacity into illegible mush. Use size or
weight for hierarchy on muted secondary text.
#### 10.6 Daisyui `btn` defaults without intent
```tsx
// Anti-pattern: just reaching for `btn` with no role:
<button className="btn">Click me</button>
// Correct: pick an archetype from §4.
<button className="btn btn-ghost">Cancel</button> // Flat
<button className="btn btn-primary">Save</button> // Accent CTA
```
Why: daisyui's `btn` default isn't tuned for any specific role. Pick from the action
vocabulary so the button signals its weight in the surface hierarchy.
#### 10.7 Ad-hoc surface tokens
```tsx
// Anti-pattern:
<div className="bg-white border-gray-200">
// Correct:
<div className="bg-base-100 border-base-200">
```
Why: hard-coded colors don't theme. Readest has 11 themes plus user-defined custom themes.
Always use the daisyui semantic tokens.
#### 10.8 Mixing `btn` sizes within a surface
```tsx
// Anti-pattern:
<header>
<button className="btn btn-sm">Search</button>
<button className="btn btn-md">Settings</button>
<button className="btn btn-xs">Close</button>
</header>
// Correct: one size per surface
<header>
<button className="btn btn-ghost btn-circle h-8 min-h-8 w-8">Search</button>
<button className="btn btn-ghost btn-circle h-8 min-h-8 w-8">Settings</button>
<button className="btn btn-ghost btn-circle h-8 min-h-8 w-8">Close</button>
</header>
```
Why: visual rhythm. Mixed sizes feel like the surface is unfinished.
---
### 11. Quick reference
When designing a new surface, walk this checklist:
1. **What's the surface tier?** Window / View / Card. (§3)
2. **What's the corner radius?** Match the tier. (§3)
3. **Is there a primary action?** If yes, ONE accent CTA. If no, all flats. (§4.1, §4.3)
4. **Are there list extensions?** Use the ListExtension archetype, not `btn-outline btn-primary`. (§4.6)
5. **Is it a list?** Use the BoxedList chassis with ActionRow / SwitchRow / ComboRow / ExpanderRow rows. (§5)
6. **Does it need `eink-bordered`?** If it has a soft border that must stay visible in
eink mode, yes. (§8)
7. **Is the hover signal localized?** One focal element changes, not the whole control. (§2.4)
8. **Is motion color-only?** No transforms unless the transform IS the message. (§2.5)
9. **Is focus visible?** `focus-visible:ring-2 focus-visible:ring-base-content/15` on
custom buttons. (§7)
10. **Will it work on the smallest theme + e-ink?** Toggle Sepia + Eink, retest.
---
### 12. Glossary
- **Adwaita / libadwaita**: GNOME's design system and widget toolkit. Source of Readest's
visual lineage.
- **AdwActionRow / AdwSwitchRow / AdwComboRow / AdwExpanderRow**: libadwaita's row
primitives. Readest mirrors these conceptually with custom React components.
- **AdwBoxedList**: libadwaita's named container for grouped action rows.
- **AdwBanner**: top-of-window inline alert (persistent).
- **AdwToast**: bottom slide-in transient alert.
- **Window / View / Card**: surface tiers (§3).
- **ListExtension**: Readest-named archetype for "+ add new row" buttons (§4.6).
- **eink-bordered**: utility class in `globals.css` that gives a surface its e-ink-mode
contrast border. Opt-in.
- **Pill ghost**: circular icon button, `btn-ghost btn-circle`.
---
### 13. Maintenance
This doc is the **source of truth** for new design decisions. When the system grows:
- New archetypes get a numbered subsection in §4 or §5.
- New anti-patterns get added to §10 with a real source reference.
- Updates to existing principles require a brief why-changed note in the relevant section.
Cross-references that must stay in sync:
- `CLAUDE.md` E-ink mode section → §8 of this doc.
- `docs/safe-area-insets.md` → §9 (cross-platform).
- `src/styles/globals.css` `[data-eink]` rules → §8.
- `src/styles/themes.ts` Palette type → §3 token table.
If you change a rule here, search for the cross-reference and update both.
+125
View File
@@ -0,0 +1,125 @@
{
"$schema": "https://biomejs.dev/schemas/2.4.9/schema.json",
"vcs": { "enabled": true, "clientKind": "git", "useIgnoreFile": true },
"files": { "ignoreUnknown": true },
"formatter": { "enabled": false },
"assist": { "enabled": false },
"css": { "linter": { "enabled": false } },
"linter": {
"enabled": true,
"includes": [
"**",
"!.next/**",
"!.open-next/**",
"!.wrangler/**",
"!.claude/**",
"!dist/**",
"!out/**",
"!build/**",
"!public/**",
"!src-tauri/**",
"!next-env.d.ts",
"!i18next-scanner.config.cjs"
],
"rules": {
"recommended": true,
"a11y": {
"recommended": true,
"useKeyWithClickEvents": "off",
"useKeyWithMouseEvents": "off",
"noSvgWithoutTitle": "off",
"noLabelWithoutControl": "off",
"useSemanticElements": "off",
"noAriaHiddenOnFocusable": "off",
"noInteractiveElementToNoninteractiveRole": "off",
"noNoninteractiveElementToInteractiveRole": "off",
"noNoninteractiveElementInteractions": "off",
"noStaticElementInteractions": "off",
"noNoninteractiveTabindex": "off",
"useButtonType": "off",
"useAriaPropsSupportedByRole": "off",
"useFocusableInteractive": "off",
"noAutofocus": "off"
},
"complexity": {
"noForEach": "off",
"noStaticOnlyClass": "off",
"noUselessSwitchCase": "off",
"noUselessFragments": "off",
"noUselessCatch": "off",
"useLiteralKeys": "off",
"useOptionalChain": "off",
"noThisInStatic": "off",
"useArrowFunction": "off",
"noUselessEscapeInRegex": "off"
},
"correctness": {
"noUnusedVariables": "warn",
"noUnusedImports": "error",
"useExhaustiveDependencies": "off",
"useHookAtTopLevel": "error",
"useJsxKeyInIterable": "error",
"noChildrenProp": "error",
"noNextAsyncClientComponent": "warn",
"noSwitchDeclarations": "off",
"noUndeclaredVariables": "off",
"noEmptyCharacterClassInRegex": "off",
"useParseIntRadix": "off",
"noEmptyPattern": "off"
},
"nursery": {
"noBeforeInteractiveScriptOutsideDocument": "warn",
"noDuplicateEnumValues": "error",
"noSyncScripts": "warn",
"useInlineScriptId": "error"
},
"performance": {
"noImgElement": "off",
"noUnwantedPolyfillio": "warn",
"useGoogleFontPreconnect": "warn"
},
"security": {
"noBlankTarget": "off",
"noDangerouslySetInnerHtmlWithChildren": "off",
"noDangerouslySetInnerHtml": "off"
},
"style": {
"noNonNullAssertion": "off",
"useImportType": "off",
"noParameterAssign": "off",
"useDefaultParameterLast": "off",
"noUselessElse": "off",
"noHeadElement": "warn",
"noCommonJs": "off",
"useFilenamingConvention": "off",
"useNamingConvention": "off",
"noUnusedTemplateLiteral": "off",
"useTemplate": "off",
"useExponentiationOperator": "off",
"useNodejsImportProtocol": "off"
},
"suspicious": {
"noExplicitAny": "error",
"noArrayIndexKey": "off",
"noAssignInExpressions": "off",
"noDoubleEquals": "off",
"noDocumentImportInPage": "error",
"noHeadImportInDocument": "error",
"useGoogleFontDisplay": "warn",
"noCommentText": "error",
"noDuplicateJsxProps": "error",
"noAsyncPromiseExecutor": "off",
"noImplicitAnyLet": "off",
"noControlCharactersInRegex": "off",
"noEmptyBlockStatements": "off",
"useIterableCallbackReturn": "off",
"noGlobalIsNan": "off",
"noConfusingVoidType": "off",
"noConstEnum": "off"
}
}
},
"javascript": {
"globals": ["React"]
}
}
+22
View File
@@ -0,0 +1,22 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "tailwind.config.ts",
"css": "src/styles/globals.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"iconLibrary": "lucide",
"aliases": {
"components": "@/components",
"utils": "@/utils",
"ui": "@/components/ui",
"lib": "@/libs",
"hooks": "@/hooks"
},
"registries": {}
}
+72
View File
@@ -0,0 +1,72 @@
## i18n Guide
Readest uses a **key-as-content** approach — English strings are the translation keys. The English locale (`en/translation.json`) is empty because keys serve as content. Other locales contain actual translations.
### In React Components
```tsx
import { useTranslation } from '@/hooks/useTranslation';
const _ = useTranslation();
_('Progress synced');
```
### In Non-React Modules
Two-step process:
**1. Declaration** — Use `stubTranslation` to mark strings for scanner extraction (returns key as-is, does NOT translate):
```ts
import { stubTranslation as _ } from '@/utils/misc';
// These calls only register keys for extraction
_('Reveal in Finder');
_('Reveal in Explorer');
```
**2. Usage** — In the React component that consumes the value, apply the real `_()` from `useTranslation`:
```tsx
const _ = useTranslation();
const label = _(getRevealLabel()); // translates at runtime
```
### Extraction & Translation
```bash
pnpm i18n:extract # Scans codebase, adds new keys with __STRING_NOT_TRANSLATED__
```
- Translation files: `public/locales/<locale>/translation.json`
- Only `_('KEY')` and `_('KEY', options)` patterns are recognized by i18next-scanner
### Adding a New Translation Language
The supported language set has a single ground truth: [`i18n-langs.json`](../i18n-langs.json). Both the i18next runtime (`src/i18n/i18n.ts`) and the extractor (`i18next-scanner.config.cjs`) read from it, so adding a locale is a two-file change plus a translation pass.
1. **Add the locale code** to [`i18n-langs.json`](../i18n-langs.json). Use the exact code i18next will emit (e.g. `hu`, `zh-CN`). Do not add `en` — it's the source language and lives outside this list.
2. **Add a display label** to `TRANSLATED_LANGS` in [`src/services/constants.ts`](../src/services/constants.ts). The key is the locale code, the value is the language's native name (e.g. `hu: 'Magyar'`). This is what users see in the language picker.
3. **Generate the translation file**:
```bash
pnpm i18n:extract
```
This creates `public/locales/<code>/translation.json` with every key set to `__STRING_NOT_TRANSLATED__`.
4. **Translate** every `__STRING_NOT_TRANSLATED__` placeholder in the new file. The `/i18n` skill automates this; the singular `en/translation.json` only holds plural variants and proper nouns, so use the JSON keys themselves as the English source.
5. **Verify** with `grep -r "__STRING_NOT_TRANSLATED__" public/locales/<code>/` — the result should be empty.
6. **Translate the KOReader companion plugin** (`apps/readest.koplugin`). It pulls the locale set from the same `i18n-langs.json` via the scanner config, but the catalog format is gettext `.po`, not JSON. Steps:
- Add a `LANG_META` entry (label + Plural-Forms) for the new code in [`apps/readest.koplugin/scripts/extract-i18n.js`](../../readest.koplugin/scripts/extract-i18n.js). Without it the extractor prints `<code> skipped (no metadata in extract-i18n.js)` and the catalog is never created.
- Trigger the `/i18n-koplugin` skill to run extraction and fill every empty `msgstr ""` in `apps/readest.koplugin/locales/<code>/translation.po`.
### Rules
- `stubTranslation` is for extraction only — always apply `_()` from `useTranslation` in the component for runtime translation.
- Fallback: when no translation exists, the English key itself is displayed.
- Error messages: register keys with `stubTranslation` in utility modules (e.g. `src/services/errors.ts`), return the English key from helpers, wrap with `_()` in the component.
+52
View File
@@ -0,0 +1,52 @@
## Safe Area Insets
The app runs on devices with notches, status bars, and rounded corners (iOS, Android). UI elements near screen edges must account for safe area insets to avoid being obscured.
### Key Concepts
- **`gridInsets: Insets`** — Per-view insets derived from view settings (header/footer visibility, margins). Calculated by `getViewInsets()` in `src/utils/insets.ts`. Passed as a prop from `BooksGrid` → child components.
- **`statusBarHeight: number`** — OS status bar height (default 24px). Stored in `themeStore`.
- **`systemUIVisible: boolean`** — Whether the system UI (status bar, navigation bar) is currently shown. Stored in `themeStore`.
- **`appService?.hasSafeAreaInset`** — Whether the platform requires safe area handling (mobile devices).
### Top Inset Rules
For UI elements anchored to the **top** of the screen (headers, close buttons, overlays):
```tsx
// When system UI is visible, use the larger of gridInsets.top and statusBarHeight
// When system UI is hidden, use gridInsets.top alone
style={{
marginTop: systemUIVisible
? `${Math.max(gridInsets.top, statusBarHeight)}px`
: `${gridInsets.top}px`,
}}
```
For containers that need safe area padding at the top:
```tsx
style={{
paddingTop: appService?.hasSafeAreaInset ? `${gridInsets.top}px` : '0px',
}}
```
### Bottom Inset Rules
For UI elements anchored to the **bottom** of the screen (footer bars, controls, progress indicators), use `gridInsets.bottom * 0.33` as padding — a fraction of the full inset since bottom bars don't need as much clearance as the home indicator area:
```tsx
style={{
paddingBottom: appService?.hasSafeAreaInset ? `${gridInsets.bottom * 0.33}px` : 0,
}}
```
### Passing `gridInsets`
When creating overlay components (image viewers, table viewers, zoom controls, etc.), always pass `gridInsets` as a prop so they can position their controls correctly:
```tsx
<ImageViewer gridInsets={gridInsets} ... />
<TableViewer gridInsets={gridInsets} ... />
<ZoomControls gridInsets={gridInsets} ... />
```
+110
View File
@@ -0,0 +1,110 @@
# Testing
Readest uses three test tiers, all powered by [Vitest](https://vitest.dev/).
## Unit Tests (`pnpm test`)
Runs tests in a **jsdom** environment. No browser or Tauri runtime required.
```bash
pnpm test # Run all unit tests
pnpm test -- src/__tests__/utils/misc.test.ts # Run a single file
pnpm test -- --watch # Watch mode
```
- **Config:** `vitest.config.mts`
- **Pattern:** `src/**/*.test.ts` (excludes `*.browser.test.ts` and `*.tauri.test.ts`)
- **Environment:** jsdom
- **Use for:** Pure logic, utilities, services that don't need real browser APIs or Tauri IPC.
## Browser Tests (`pnpm test:browser`)
Runs tests in a **real Chromium** browser via Playwright. Required for code that depends on Web Workers, SharedArrayBuffer, OPFS, or other browser-only APIs.
```bash
pnpm test:browser
```
- **Config:** `vitest.browser.config.mts`
- **Pattern:** `src/**/*.browser.test.ts`
- **Browser:** Chromium (headless, via `@vitest/browser-playwright`)
- **Use for:** WASM modules (e.g. `@tursodatabase/database-wasm`), Web Worker integration, browser-specific storage APIs.
## Tauri Integration Tests (`pnpm test:tauri`)
Runs Vitest tests **inside the Tauri WebView**, with access to Tauri IPC and native plugin commands. Tests execute in the actual app environment.
### Step 1: Start the Tauri App
In one terminal, start the app with the `webdriver` feature enabled:
```bash
pnpm tauri:dev:test # Dev mode (uses tauri dev server, faster iteration)
pnpm tauri:build:test # Debug release build (closer to production)
```
These commands compile the Rust backend with `--features webdriver`, which:
- Includes `tauri-plugin-webdriver` (embeds a W3C WebDriver server on port 4445)
- Adds a runtime capability granting plugin permissions to remote URLs (`http://127.0.0.1:*`), so Vitest's browser-mode iframe can call Tauri IPC
Keep this running while you run tests.
### Step 2: Run Tests
In another terminal:
```bash
pnpm test:tauri
```
Vitest connects directly to the embedded WebDriver server (port 4445) in the running Tauri app and executes tests inside its WebView.
- **Config:** `vitest.tauri.config.mts`
- **Pattern:** `src/**/*.tauri.test.ts`
- **Browser provider:** `@vitest/browser-webdriverio` (connects to port 4445)
- **Use for:** Tauri plugin commands (turso, native-tts, etc.), native filesystem, Tauri IPC.
### Writing Tauri Tests
Tests access Tauri IPC via a shared helper:
```typescript
import { invoke } from '../tauri/tauri-invoke';
it('calls a plugin command', async () => {
const result = await invoke('plugin:turso|load', { options: { path: 'sqlite::memory:' } });
expect(result).toBeDefined();
});
```
The `invoke()` helper accesses `window.top.__TAURI_INTERNALS__` (Vitest runs in an iframe, Tauri injects IPC into the main frame).
**Limitations:** Only custom invoke commands and plugin commands listed in the webdriver capability work. Standard Tauri JS APIs (e.g. `@tauri-apps/api`) that rely on `URL: local` may not work from the Vitest iframe.
## E2E Tests (WDIO)
Full end-to-end tests using WebDriverIO, for UI-level testing against the running Tauri app. Same two-step workflow as Tauri integration tests.
```bash
# Terminal 1: start the app (same as for Tauri integration tests)
pnpm tauri:dev:test
# Terminal 2: run E2E tests
pnpm test:e2e
```
- **Config:** `wdio.conf.ts`
- **Pattern:** `e2e/**/*.e2e.ts`
- **Framework:** Mocha (via `@wdio/mocha-framework`)
- **Connects to:** port 4445 (embedded WebDriver server)
- **Use for:** UI interaction tests, window management, navigation flows.
## Test File Naming
| Suffix | Runner | Environment |
| ------------------- | ------------------- | --------------------- |
| `*.test.ts` | `pnpm test` | jsdom |
| `*.browser.test.ts` | `pnpm test:browser` | Chromium (Playwright) |
| `*.tauri.test.ts` | `pnpm test:tauri` | Tauri WebView |
| `*.e2e.ts` | `pnpm test:e2e` | Tauri app (WDIO) |
+209
View File
@@ -0,0 +1,209 @@
## Adding a Config to `ViewSettings`
`ViewSettings` is the per-book view state (layout, fonts, colors, TTS, etc.) composed from several sub-interfaces defined in `src/types/book.ts`. A matching `globalViewSettings` lives on `SystemSettings` and acts as the default for every book. The per-book value is derived by merging the global defaults with any overrides stored on the book's `BookConfig`.
This doc covers how to plumb a new config through the three layers:
1. **Types**`src/types/book.ts`
2. **Defaults**`src/services/constants.ts` and `src/services/settingsService.ts`
3. **Read/write** — components via `saveViewSettings` from `src/helpers/settings.ts`
### Pick a Pattern
**Pattern A — add a field to an existing sub-interface.** Use when the new option belongs to an existing bundle (`BookLayout`, `BookStyle`, `BookFont`, `ViewConfig`, `TTSConfig`, etc.).
**Pattern B — introduce a new sub-interface.** Use when several related fields cluster together, or when a single field is semantically its own concept (e.g. `ParagraphModeConfig`, `ViewSettingsConfig`). Then extend `ViewSettings` with it.
Both patterns follow the same three-layer flow. The only difference is whether you reuse an existing `DEFAULT_*` constant or add a new one.
### Step 1 — Declare the Type
**Pattern A** — add a required field to the sub-interface that owns this concern:
```ts
// src/types/book.ts
export interface ViewConfig {
// ...existing fields
myNewToggle: boolean;
}
```
**Pattern B** — define a new interface and extend `ViewSettings`:
```ts
// src/types/book.ts
export interface ViewSettingsConfig {
isGlobal: boolean;
}
export interface ViewSettings
extends
BookLayout,
BookStyle,
// ...other bundles
ViewSettingsConfig {}
```
Fields should be **required**, not optional. Optional fields make downstream code defensive. Provide a sensible default in Step 2 instead.
### Step 2 — Provide a Default
Every field in `ViewSettings` must have a default, otherwise `getDefaultViewSettings()` produces an incomplete object.
**Pattern A** — add the value to the existing `DEFAULT_*` constant:
```ts
// src/services/constants.ts
export const DEFAULT_VIEW_CONFIG: ViewConfig = {
// ...existing defaults
myNewToggle: false,
};
```
**Pattern B** — add a `DEFAULT_*_CONFIG` constant for your new bundle, then register it in `getDefaultViewSettings`:
```ts
// src/services/constants.ts
export const DEFAULT_VIEW_SETTINGS_CONFIG: ViewSettingsConfig = {
isGlobal: true,
};
```
```ts
// src/services/settingsService.ts
export function getDefaultViewSettings(ctx: Context): ViewSettings {
return {
...DEFAULT_BOOK_LAYOUT,
...DEFAULT_BOOK_STYLE,
// ...other bundles
...DEFAULT_VIEW_SETTINGS_CONFIG,
// platform overrides go last so they win
...(ctx.isMobile ? DEFAULT_MOBILE_VIEW_SETTINGS : {}),
...(ctx.isEink ? DEFAULT_EINK_VIEW_SETTINGS : {}),
...(isCJKEnv() ? DEFAULT_CJK_VIEW_SETTINGS : {}),
};
}
```
#### Platform Overrides
To tweak the default on mobile, e-ink, or CJK locales, add the field to the matching `Partial<ViewSettings>` constant (`DEFAULT_MOBILE_VIEW_SETTINGS`, `DEFAULT_EINK_VIEW_SETTINGS`, `DEFAULT_CJK_VIEW_SETTINGS`). These are spread after the base defaults in `getDefaultViewSettings`, so they override them.
#### Migration
Old `settings.json` files on disk won't have your new field. `loadSettings` merges the stored blob over fresh defaults:
```ts
settings.globalViewSettings = {
...getDefaultViewSettings(ctx),
...settings.globalViewSettings,
};
```
So existing users pick up your default automatically — no explicit migration is needed for adding a field. Only bump `SYSTEM_SETTINGS_VERSION` if you are reshaping existing data.
### Step 3 — Read and Write from Components
Read the current value by preferring the per-book settings, falling back to the global:
```tsx
const { settings } = useSettingsStore();
const { getViewSettings } = useReaderStore();
const viewSettings = getViewSettings(bookKey) || settings.globalViewSettings;
```
Write via `saveViewSettings` — never mutate the store directly. The helper handles the global-vs-per-book routing, persists to disk, and re-applies styles when needed.
```tsx
import { saveViewSettings } from '@/helpers/settings';
const [myNewToggle, setMyNewToggle] = useState(viewSettings.myNewToggle);
useEffect(() => {
saveViewSettings(envConfig, bookKey, 'myNewToggle', myNewToggle);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [myNewToggle]);
```
The `useEffect`-on-local-state pattern is the established convention in `LayoutPanel`, `ControlPanel`, `ColorPanel`, etc. It keeps the UI responsive and batches store updates until the user stops interacting.
#### Signature
```ts
saveViewSettings<K extends keyof ViewSettings>(
envConfig,
bookKey,
key: K,
value: ViewSettings[K],
skipGlobal = false, // true → only update this book's settings
applyStyles = true, // false → don't re-run style recomputation
)
```
**Global vs. per-book routing.** `saveViewSettings` inspects `viewSettings.isGlobal` on the target book. When `true` (the default), it writes to `globalViewSettings`, loops through every open book, and saves to disk. When `false`, it writes only to the one book's config.
**Skip global.** Pass `skipGlobal=true` when the setting is meta — i.e. it describes the settings system itself, not book content. The canonical case is toggling `isGlobal` from `DialogMenu`: you want the scope flag to live on the specific book without propagating it to every other book.
```tsx
saveViewSettings(envConfig, bookKey, 'isGlobal', !isSettingsGlobal, true, false);
```
**Skip styles.** Pass `applyStyles=false` for options that don't affect CSS rendering (toggles, flags, metadata). This avoids an unnecessary `renderer.setStyles` call.
### Step 4 — Support Reset
If your field should be resettable from the panel menu, register a setter in the panel's `handleReset` via `useResetViewSettings`:
```tsx
const resetToDefaults = useResetViewSettings();
const handleReset = () => {
resetToDefaults({
myNewToggle: setMyNewToggle,
// ...other setters
});
};
```
The hook resolves the default by reading from `getDefaultViewSettings(ctx)` and calls each provided setter with that value, which then fires your `useEffect` and persists the change.
### Step 5 — Register in the Command Palette
If your setting has a visible row in a panel, register it in the matching `*PanelItems` array in `src/services/commandRegistry.ts`. This wires it into the command-palette fuzzy search so users can jump straight to it.
```ts
// src/services/commandRegistry.ts
const layoutPanelItems = [
// ...existing entries
{
id: 'settings.layout.myNewToggle',
labelKey: _('My New Toggle'),
keywords: ['search', 'terms', 'for', 'discoverability'],
section: 'Paragraph',
},
];
```
- `id` must match the `data-setting-id` attribute on the panel row. The palette uses it to scroll/highlight the target control.
- `labelKey` uses `stubTranslation` (imported as `_`) so the extractor picks it up — the same string that appears in the panel.
- `keywords` broadens fuzzy-search hits beyond the label; include synonyms, related jargon, and the panel section name.
- `section` groups the entry in the palette results (matches the panel's sub-header: `Layout`, `Paragraph`, `Page`, `Header & Footer`, etc.).
Skip this step only for settings that don't surface as a user-visible row (hidden toggles, flags used by other settings).
### Don'ts
- **Don't make the field optional** just to skip providing a default. Add a default in Step 2 instead.
- **Don't mutate `settings.globalViewSettings` directly** in a component — `saveViewSettings` already handles global propagation when `isGlobal` is true.
- **Don't bump `SYSTEM_SETTINGS_VERSION`** for a plain additive field. The load-time merge handles it.
### Minimal Checklist
- [ ] Field or new interface added in `src/types/book.ts`
- [ ] Default value in `src/services/constants.ts`
- [ ] New `DEFAULT_*_CONFIG` spread into `getDefaultViewSettings` (Pattern B only)
- [ ] Optional mobile/eink/CJK override in the matching `Partial<ViewSettings>` constant
- [ ] Read via `getViewSettings(bookKey) || settings.globalViewSettings`
- [ ] Write via `saveViewSettings(envConfig, bookKey, 'key', value)`
- [ ] Reset setter wired into `useResetViewSettings` if the panel has a reset menu
- [ ] Command-palette entry added to the matching `*PanelItems` array in `src/services/commandRegistry.ts`, with an `id` that matches the panel row's `data-setting-id`
+123
View File
@@ -0,0 +1,123 @@
describe('Readest App Launch', () => {
it('should have a visible body element', async () => {
const body = await $('body');
await body.waitForDisplayed({ timeout: 10000 });
expect(await body.isDisplayed()).toBe(true);
});
it('should have the correct window handle', async () => {
const handle = await browser.getWindowHandle();
expect(handle).toBeTruthy();
});
it('should return the page source', async () => {
const source = await browser.getPageSource();
expect(source).toContain('html');
});
});
describe('Library Page', () => {
it('should navigate to the library page', async () => {
const url = await browser.getUrl();
expect(url).toMatch(/library|localhost/);
});
it('should display the library container', async () => {
const library = await $('[aria-label="Your Library"]');
await library.waitForExist({ timeout: 15000 });
expect(await library.isExisting()).toBe(true);
});
it('should display the library header', async () => {
const header = await $('[aria-label="Library Header"]');
await header.waitForExist({ timeout: 10000 });
expect(await header.isExisting()).toBe(true);
});
it('should display the bookshelf area', async () => {
const bookshelf = await $('[aria-label="Bookshelf"]');
await bookshelf.waitForExist({ timeout: 10000 });
expect(await bookshelf.isExisting()).toBe(true);
});
it('should have a search input', async () => {
const searchInput = await $('.search-input');
await searchInput.waitForExist({ timeout: 10000 });
expect(await searchInput.isExisting()).toBe(true);
});
it('should allow typing in the search input', async () => {
const searchInput = await $('.search-input');
await searchInput.waitForDisplayed({ timeout: 10000 });
await searchInput.setValue('test search');
const value = await searchInput.getValue();
expect(value).toBe('test search');
});
it('should show the clear search button after typing', async () => {
const clearBtn = await $('[aria-label="Clear Search"]');
await clearBtn.waitForExist({ timeout: 5000 });
expect(await clearBtn.isExisting()).toBe(true);
});
it('should clear the search input when clear button is clicked', async () => {
const clearBtn = await $('[aria-label="Clear Search"]');
await clearBtn.click();
const searchInput = await $('.search-input');
const value = await searchInput.getValue();
expect(value).toBe('');
});
it('should have a select books button', async () => {
const selectBtn = await $('[aria-label="Select Books"]');
await selectBtn.waitForExist({ timeout: 10000 });
expect(await selectBtn.isExisting()).toBe(true);
});
it('should have an import books button', async () => {
const importBtn = await $('[aria-label="Import Books"]');
await importBtn.waitForExist({ timeout: 10000 });
expect(await importBtn.isExisting()).toBe(true);
});
});
describe('Window Management', () => {
it('should return the window size', async () => {
const size = await browser.getWindowSize();
expect(size.width).toBeGreaterThan(0);
expect(size.height).toBeGreaterThan(0);
});
});
describe('JavaScript Execution', () => {
it('should execute JavaScript in the app context', async () => {
const result = await browser.execute(() => {
return document.readyState;
});
expect(result).toBe('complete');
});
it('should access the document title via JS', async () => {
const title = await browser.execute(() => {
return document.title;
});
expect(title).toContain('Readest');
});
it('should detect the app platform globals', async () => {
const hasCLIAccess = await browser.execute(() => {
return (window as unknown as Record<string, unknown>).__READEST_CLI_ACCESS === true;
});
expect(hasCLIAccess).toBe(true);
});
});
describe('Navigation', () => {
it('should navigate back to library after visiting another route', async () => {
const currentUrl = await browser.getUrl();
await browser.url(currentUrl.replace(/\/[^/]*$/, '/library'));
const library = await $('[aria-label="Your Library"]');
await library.waitForExist({ timeout: 15000 });
expect(await library.isExisting()).toBe(true);
});
});
+11
View File
@@ -0,0 +1,11 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"skipLibCheck": true,
"types": ["mocha", "@wdio/globals/types"]
},
"include": ["./**/*.ts"]
}
-35
View File
@@ -1,35 +0,0 @@
import { defineConfig, globalIgnores } from 'eslint/config';
import next from 'eslint-config-next';
import nextVitals from 'eslint-config-next/core-web-vitals';
import tseslint from 'eslint-config-next/typescript';
import jsxA11y from 'eslint-plugin-jsx-a11y';
const eslintConfig = defineConfig([
...tseslint,
...next,
...nextVitals,
{
rules: {
...jsxA11y.configs.recommended.rules,
'@typescript-eslint/no-unused-vars': [
'warn',
{
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
caughtErrorsIgnorePattern: '^_',
},
],
},
},
globalIgnores([
'node_modules/**',
'.next/**',
'.open-next/**',
'out/**',
'build/**',
'public/**',
'next-env.d.ts',
]),
]);
export default eslintConfig;
@@ -12,28 +12,31 @@ This crate provides Windows Explorer thumbnail support for eBook files when Read
## Supported Formats
| Format | Extension | Cover Source |
|--------|-----------|--------------|
| EPUB | `.epub` | OPF manifest cover reference |
| MOBI/AZW | `.mobi`, `.azw`, `.prc` | EXTH cover offset |
| AZW3/KF8 | `.azw3`, `.kf8` | KF8 format cover |
| FB2 | `.fb2` | `<binary>` coverpage element |
| Comic Book | `.cbz`, `.cbr` | First image in archive |
| Plain Text | `.txt` | Generated placeholder |
| Format | Extension | Cover Source |
| ---------- | ----------------------- | ---------------------------- |
| EPUB | `.epub` | OPF manifest cover reference |
| MOBI/AZW | `.mobi`, `.azw`, `.prc` | EXTH cover offset |
| AZW3/KF8 | `.azw3`, `.kf8` | KF8 format cover |
| FB2 | `.fb2` | `<binary>` coverpage element |
| Comic Book | `.cbz`, `.cbr` | First image in archive |
| Plain Text | `.txt` | Generated placeholder |
## Building
### Library Only
```bash
cargo build --release
```
### COM DLL (for Windows Explorer integration)
```bash
cargo build --release --features com
```
### CLI Tool
```bash
cargo build --release --features cli
```
+35
View File
@@ -0,0 +1,35 @@
[
"de",
"ja",
"es",
"fa",
"fr",
"it",
"el",
"ko",
"uk",
"nl",
"sl",
"sv",
"pl",
"pt",
"ru",
"tr",
"hi",
"id",
"vi",
"ms",
"he",
"ar",
"th",
"bo",
"bn",
"ta",
"si",
"ro",
"hu",
"uz",
"pt-BR",
"zh-CN",
"zh-TW"
]
@@ -0,0 +1,35 @@
const lngs = require('./i18n-langs.json');
const options = {
debug: false,
sort: false,
func: {
list: ['_'],
extensions: ['.js', '.jsx', '.ts', '.tsx'],
},
lngs,
ns: ['translation'],
defaultNs: 'translation',
defaultValue: '__STRING_NOT_TRANSLATED__',
resource: {
loadPath: './public/locales/{{lng}}/{{ns}}.json',
savePath: './public/locales/{{lng}}/{{ns}}.json',
jsonIndent: 2,
lineEnding: '\n',
},
keySeparator: false,
nsSeparator: false,
interpolation: {
prefix: '{{',
suffix: '}}',
},
metadata: {},
allowDynamicKeys: true,
removeUnusedKeys: true,
};
module.exports = {
input: ['src/**/*.{js,jsx,ts,tsx}', '!src/**/*.test.{js,jsx,ts,tsx}'],
output: '.',
options,
};
@@ -1,59 +0,0 @@
module.exports = {
input: ['src/**/*.{js,jsx,ts,tsx}', '!src/**/*.test.{js,jsx,ts,tsx}'],
output: '.',
options: {
debug: false,
sort: false,
func: {
list: ['_'],
extensions: ['.js', '.jsx', '.ts', '.tsx'],
},
lngs: [
'de',
'ja',
'es',
'fa',
'fr',
'it',
'el',
'ko',
'uk',
'nl',
'sv',
'pl',
'pt',
'ru',
'tr',
'hi',
'id',
'vi',
'ms',
'ar',
'th',
'bo',
'bn',
'ta',
'si',
'zh-CN',
'zh-TW',
],
ns: ['translation'],
defaultNs: 'translation',
defaultValue: '__STRING_NOT_TRANSLATED__',
resource: {
loadPath: './public/locales/{{lng}}/{{ns}}.json',
savePath: './public/locales/{{lng}}/{{ns}}.json',
jsonIndent: 2,
lineEnding: '\n',
},
keySeparator: false,
nsSeparator: false,
interpolation: {
prefix: '{{',
suffix: '}}',
},
metadata: {},
allowDynamicKeys: true,
removeUnusedKeys: true,
},
};
+80 -48
View File
@@ -1,5 +1,9 @@
import withPWAInit from '@ducanh2912/next-pwa';
import withSerwistInit from '@serwist/next';
import withBundleAnalyzer from '@next/bundle-analyzer';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const isDev = process.env['NODE_ENV'] === 'development';
const appPlatform = process.env['NEXT_PUBLIC_APP_PLATFORM'];
@@ -27,19 +31,65 @@ const nextConfig = {
assetPrefix: '',
reactStrictMode: true,
serverExternalPackages: ['isows'],
turbopack: {},
transpilePackages: !isDev
? [
'i18next-browser-languagedetector',
'react-i18next',
'i18next',
'@ducanh2912/next-pwa',
'@tauri-apps',
'highlight.js',
'foliate-js',
'marked',
]
: [],
allowedDevOrigins: ['192.168.2.120'],
webpack: (config) => {
config.resolve.alias = {
...config.resolve.alias,
nunjucks: 'nunjucks/browser/nunjucks.js',
// `js-mdict` is consumed as TS source via tsconfig paths from
// `packages/js-mdict/src/`; its sources `import 'fflate'` directly.
// Without an alias, webpack walks up from that source location and
// can't find fflate (only installed in this app's node_modules).
fflate: path.resolve(__dirname, 'node_modules/fflate'),
...(appPlatform !== 'web' ? { '@tursodatabase/database-wasm': false } : {}),
};
return config;
},
turbopack: {
resolveAlias: {
nunjucks: 'nunjucks/browser/nunjucks.js',
// Turbopack rejects absolute paths in resolveAlias ("server relative
// imports not implemented") — use a project-relative path.
fflate: './node_modules/fflate',
...(appPlatform !== 'web' ? { '@tursodatabase/database-wasm': './src/utils/stub.ts' } : {}),
},
},
transpilePackages: [
'ai',
'ai-sdk-ollama',
'@ai-sdk/react',
'@assistant-ui/react',
'@assistant-ui/react-ai-sdk',
'@assistant-ui/react-markdown',
'streamdown',
...(isDev
? []
: [
'i18next-browser-languagedetector',
'react-i18next',
'i18next',
'@tauri-apps',
'highlight.js',
'foliate-js',
'marked',
]),
],
async rewrites() {
return [
{
source: '/reader/:ids',
destination: '/reader?ids=:ids',
},
{
source: '/o/book/:hash/annotation/:id',
destination: '/o?book=:hash&note=:id',
},
{
source: '/s/:token',
destination: '/s?token=:token',
},
];
},
async headers() {
return [
{
@@ -56,7 +106,9 @@ const nextConfig = {
headers: [
{
key: 'Cache-Control',
value: 'public, max-age=31536000, immutable',
value: isDev
? 'public, max-age=0, must-revalidate'
: 'public, max-age=31536000, immutable',
},
],
},
@@ -64,39 +116,19 @@ const nextConfig = {
},
};
const withPWA = withPWAInit({
dest: 'public',
disable: isDev || appPlatform !== 'web',
cacheStartUrl: false,
dynamicStartUrl: false,
cacheOnFrontEndNav: true,
aggressiveFrontEndNavCaching: true,
reloadOnOnline: true,
swcMinify: true,
fallbacks: {
document: '/offline',
},
workboxOptions: {
disableDevLogs: true,
manifestTransforms: [
(manifestEntries) => {
const manifest = manifestEntries.filter((entry) => {
const url = entry.url;
return (
!url.includes('dynamic-css-manifest.json') &&
!url.includes('middleware-manifest.json') &&
!url.includes('react-loadable-manifest.json') &&
!url.includes('build-manifest.json') &&
!url.includes('_buildManifest.js') &&
!url.includes('_ssgManifest.js') &&
!url.includes('_headers')
);
});
return { manifest };
},
],
},
});
const pwaDisabled = isDev || appPlatform !== 'web';
const withPWA = pwaDisabled
? (config) => config
: withSerwistInit({
swSrc: 'src/sw.ts',
swDest: 'public/sw.js',
cacheOnNavigation: true,
reloadOnOnline: true,
disable: false,
register: true,
scope: '/',
});
const withAnalyzer = withBundleAnalyzer({
enabled: process.env.ANALYZE === 'true',
+144 -49
View File
@@ -1,7 +1,8 @@
{
"name": "@readest/readest-app",
"version": "0.9.94",
"version": "0.11.1",
"private": true,
"type": "module",
"scripts": {
"dev": "dotenv -e .env.tauri -- next dev",
"build": "dotenv -e .env.tauri -- next build",
@@ -9,22 +10,45 @@
"dev-web": "dotenv -e .env.web -- next dev",
"build-web": "dotenv -e .env.web -- next build",
"start-web": "dotenv -e .env.web -- next start",
"dev-web:vinext": "dotenv -e .env.web -- vinext dev",
"build-web:vinext": "dotenv -e .env.web -- vinext build",
"start-web:vinext": "dotenv -e .env.web -- vinext start",
"build-tauri": "dotenv -e .env.tauri -- next build",
"i18n:extract": "i18next-scanner",
"lint": "eslint .",
"test": "dotenv -e .env -e .env.test.local vitest",
"dev-android": "tauri android build -t aarch64 -- --features devtools && adb install -r src-tauri/gen/android/app/build/outputs/apk/universal/release/app-universal-release.apk",
"dev-ios": "tauri ios build -- --features devtools && ideviceinstaller -i src-tauri/gen/apple/build/arm64/Readest.ipa",
"i18n:extract": "i18next-scanner --config i18next-scanner.config.cjs",
"lint": "tsgo --noEmit && biome check . && pnpm lint:lua",
"lint:lua": "node ../readest.koplugin/scripts/lint-koplugin.mjs",
"test:lua": "node ../readest.koplugin/scripts/test-koplugin.mjs",
"test": "dotenv -e .env -e .env.test.local -- vitest",
"test:coverage": "dotenv -e .env -e .env.test.local -- vitest run --coverage",
"test:browser": "dotenv -e .env -e .env.test.local -- vitest run --config vitest.browser.config.mts",
"test:tauri": "bash scripts/test-tauri.sh",
"test:pr:web": "pnpm test -- --watch=false && pnpm test:browser",
"test:pr:tauri": "bash scripts/test-tauri.sh",
"test:all": "pnpm test -- --watch=false && pnpm test:browser && bash scripts/test-tauri.sh",
"test:e2e": "wdio run wdio.conf.ts",
"tauri:dev:test": "dotenv -e .env.tauri -- tauri dev --features webdriver",
"tauri:build:test": "dotenv -e .env.tauri -- tauri build --debug --features webdriver",
"tauri": "tauri",
"prepare-public-vendor": "mkdirp ./public/vendor/pdfjs ./public/vendor/simplecc",
"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",
"fmt:check": "cargo fmt -p Readest --check",
"clippy:check": "cargo clippy -p Readest --no-deps -- -D warnings",
"format": "pnpm -w format",
"format:check": "pnpm -w format:check",
"prepare-public-vendor": "mkdirp ./public/vendor/pdfjs ./public/vendor/simplecc ./public/vendor/jieba",
"copy-pdfjs-js": "cpx \"../../packages/foliate-js/node_modules/pdfjs-dist/legacy/build/{pdf.worker.min.mjs,pdf.min.mjs,pdf.d.mts}\" ./public/vendor/pdfjs",
"copy-pdfjs-wasm": "cpx \"../../packages/foliate-js/node_modules/pdfjs-dist/wasm/{openjpeg.wasm,qcms_bg.wasm}\" ./public/vendor/pdfjs",
"copy-pdfjs-fonts": "cpx \"../../packages/foliate-js/node_modules/pdfjs-dist/{cmaps,standard_fonts}/*\" ./public/vendor/pdfjs",
"copy-flatten-pdfjs-annotation-layer-css": "npx postcss \"../../packages/foliate-js/vendor/pdfjs/annotation_layer_builder.css\" --no-map -u postcss-nested > ./public/vendor/pdfjs/annotation_layer_builder.css",
"copy-flatten-pdfjs-text-layer-css": "npx postcss \"../../packages/foliate-js/vendor/pdfjs/text_layer_builder.css\" --no-map -u postcss-nested > ./public/vendor/pdfjs/text_layer_builder.css",
"copy-flatten-pdfjs-css": "pnpm copy-flatten-pdfjs-annotation-layer-css && pnpm copy-flatten-pdfjs-text-layer-css",
"copy-pdfjs": "pnpm copy-pdfjs-js && pnpm copy-pdfjs-fonts && pnpm copy-flatten-pdfjs-css",
"copy-pdfjs": "pnpm copy-pdfjs-js && pnpm copy-pdfjs-wasm && pnpm copy-pdfjs-fonts && pnpm copy-flatten-pdfjs-css",
"copy-simplecc": "cpx \"../../packages/simplecc-wasm/dist/web/*\" ./public/vendor/simplecc",
"copy-jieba": "cpx \"./node_modules/jieba-wasm/pkg/web/*\" ./public/vendor/jieba",
"setup-pdfjs": "pnpm prepare-public-vendor && pnpm copy-pdfjs",
"setup-simplecc": "pnpm prepare-public-vendor && pnpm copy-simplecc",
"setup-vendors": "pnpm setup-pdfjs && pnpm setup-simplecc",
"setup-jieba": "pnpm prepare-public-vendor && pnpm copy-jieba",
"setup-vendors": "pnpm setup-pdfjs && pnpm setup-simplecc && pnpm setup-jieba",
"build-win-x64": "dotenv -e .env.tauri.local -- tauri build --target i686-pc-windows-msvc --bundles nsis",
"build-win-arm64": "dotenv -e .env.tauri.local -- tauri build --target aarch64-pc-windows-msvc --bundles nsis",
"build-linux-x64": "dotenv -e .env.tauri.local -- tauri build --target x86_64-unknown-linux-gnu --bundles appimage",
@@ -37,7 +61,7 @@
"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": "pnpm patch-build-webpack && NEXT_PUBLIC_APP_PLATFORM=web opennextjs-cloudflare build && pnpm restore-build-original && opennextjs-cloudflare preview --ip 0.0.0.0",
"preview": "pnpm patch-build-webpack && NEXT_PUBLIC_APP_PLATFORM=web opennextjs-cloudflare build && pnpm restore-build-original && opennextjs-cloudflare preview --ip 0.0.0.0 --port 3001",
"deploy": "pnpm patch-build-webpack && NEXT_PUBLIC_APP_PLATFORM=web opennextjs-cloudflare build && pnpm restore-build-original && opennextjs-cloudflare deploy",
"upload": "pnpm patch-build-webpack && NEXT_PUBLIC_APP_PLATFORM=web opennextjs-cloudflare build && pnpm restore-build-original && opennextjs-cloudflare upload",
"cf-typegen": "wrangler types --env-interface CloudflareEnv cloudflare-env.d.ts",
@@ -48,111 +72,182 @@
"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:lookbehind-regex": "count=$(grep -rnoE '\\(\\?<[!=]' .next/static/chunks/* out/_next/static/chunks/* | wc -l); if [ \"$count\" -gt 0 ]; then echo '❌ Lookbehind regex found in output!'; exit 1; else echo '✅ No lookbehind regex found.'; fi",
"check:all": "pnpm check:translations && pnpm check:lookbehind-regex",
"build-check": "pnpm build && pnpm build-web && pnpm check:all"
"build-check": "pnpm build && pnpm build-web && pnpm check:all",
"worktree:new": "pnpm exec tsx scripts/worktree-new.ts",
"worktree:rm": "pnpm exec tsx scripts/worktree-rm.ts"
},
"dependencies": {
"@aws-sdk/client-s3": "^3.735.0",
"@aws-sdk/s3-request-presigner": "^3.735.0",
"@ducanh2912/next-pwa": "^10.2.9",
"@ai-sdk/react": "^3.0.49",
"@assistant-ui/react": "0.11.56",
"@assistant-ui/react-ai-sdk": "1.1.21",
"@assistant-ui/react-markdown": "0.11.9",
"@aws-sdk/client-s3": "^3.1000.0",
"@aws-sdk/s3-request-presigner": "^3.1000.0",
"@choochmeque/tauri-plugin-sharekit-api": "^0.3.0",
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@emnapi/core": "^1.7.1",
"@emnapi/runtime": "^1.7.1",
"@fabianlars/tauri-plugin-oauth": "2",
"@opennextjs/cloudflare": "^1.13.1",
"@stripe/react-stripe-js": "^3.7.0",
"@stripe/stripe-js": "^7.4.0",
"@napi-rs/wasm-runtime": "^1.1.1",
"@opennextjs/cloudflare": "^1.19.4",
"@radix-ui/react-collapsible": "^1.1.12",
"@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-dropdown-menu": "^2.1.16",
"@radix-ui/react-hover-card": "^1.1.15",
"@radix-ui/react-scroll-area": "^1.2.10",
"@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-separator": "^1.1.8",
"@radix-ui/react-slot": "^1.2.4",
"@radix-ui/react-tabs": "^1.1.13",
"@radix-ui/react-tooltip": "^1.2.8",
"@serwist/next": "^9.5.6",
"@serwist/webpack-plugin": "^9.5.6",
"@stripe/react-stripe-js": "^6.2.0",
"@stripe/stripe-js": "^9.3.1",
"@supabase/auth-ui-react": "^0.4.7",
"@supabase/auth-ui-shared": "^0.1.8",
"@supabase/supabase-js": "^2.76.1",
"@tauri-apps/api": "2.9.0",
"@tauri-apps/api": "2.10.1",
"@tauri-apps/plugin-cli": "^2.4.1",
"@tauri-apps/plugin-deep-link": "^2.4.5",
"@tauri-apps/plugin-dialog": "^2.4.2",
"@tauri-apps/plugin-fs": "^2.4.4",
"@tauri-apps/plugin-deep-link": "^2.4.7",
"@tauri-apps/plugin-dialog": "^2.6.0",
"@tauri-apps/plugin-fs": "^2.4.5",
"@tauri-apps/plugin-haptics": "^2.3.2",
"@tauri-apps/plugin-http": "^2.5.4",
"@tauri-apps/plugin-log": "^2.7.1",
"@tauri-apps/plugin-opener": "^2.5.2",
"@tauri-apps/plugin-http": "^2.5.7",
"@tauri-apps/plugin-log": "^2.8.0",
"@tauri-apps/plugin-opener": "^2.5.3",
"@tauri-apps/plugin-os": "^2.3.2",
"@tauri-apps/plugin-process": "^2.3.1",
"@tauri-apps/plugin-shell": "~2.3.3",
"@tauri-apps/plugin-updater": "^2.9.0",
"@zip.js/zip.js": "^2.7.53",
"@tauri-apps/plugin-shell": "~2.3.5",
"@tauri-apps/plugin-updater": "^2.10.0",
"@tauri-apps/plugin-websocket": "~2.4.2",
"@readest/turso-database-common": "0.6.0-pre.28-readest.0",
"@readest/turso-database-wasm-common": "0.6.0-pre.28-readest.0",
"@readest/turso-database-wasm": "0.6.0-pre.28-readest.0",
"@tybys/wasm-util": "^0.10.1",
"@zip.js/zip.js": "^2.8.16",
"abortcontroller-polyfill": "^1.7.8",
"ai": "^6.0.47",
"ai-sdk-ollama": "^3.2.0",
"app-store-server-api": "^0.17.1",
"aws4fetch": "^1.0.20",
"buffer": "^6.0.3",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk": "^1.1.1",
"cors": "^2.8.5",
"dayjs": "^1.11.13",
"dompurify": "^3.3.0",
"dompurify": "^3.4.0",
"fflate": "^0.8.2",
"foliate-js": "workspace:*",
"franc-min": "^6.2.0",
"google-auth-library": "^10.4.1",
"fzf": "^0.5.2",
"google-auth-library": "^10.5.0",
"googleapis": "^164.1.0",
"highlight.js": "^11.11.1",
"i18next": "^24.2.0",
"i18next-browser-languagedetector": "^8.0.2",
"i18next-http-backend": "^3.0.1",
"i18next-http-backend": "^3.0.5",
"iso-639-2": "^3.0.2",
"iso-639-3": "^3.0.1",
"isomorphic-ws": "^5.0.0",
"jieba-wasm": "^2.4.0",
"js-md5": "^0.8.3",
"jwt-decode": "^4.0.0",
"lucide-react": "^0.562.0",
"lunr": "^2.3.9",
"marked": "^15.0.12",
"next": "16.0.3",
"nanoid": "^5.1.6",
"next": "16.2.3",
"next-view-transitions": "^0.3.5",
"nunjucks": "^3.2.4",
"overlayscrollbars": "^2.11.4",
"overlayscrollbars-react": "^0.5.6",
"posthog-js": "^1.246.0",
"react": "19.2.0",
"react": "19.2.5",
"react-color": "^2.19.3",
"react-dom": "19.2.0",
"react-dom": "19.2.5",
"react-i18next": "^15.2.0",
"react-icons": "^5.4.0",
"react-responsive": "^10.0.0",
"react-virtuoso": "^4.17.0",
"react-window": "^1.8.11",
"remark-gfm": "^4.0.1",
"semver": "^7.7.1",
"stripe": "^18.2.1",
"streamdown": "^1.6.10",
"stripe": "^22.0.2",
"styled-jsx": "^5.1.7",
"tailwind-merge": "^3.4.0",
"tauri-plugin-device-info-api": "^1.0.1",
"tinycolor2": "^1.6.0",
"uuid": "^11.1.0",
"uuid": "^14.0.0",
"ws": "^8.18.3",
"zod": "^4.0.8",
"zustand": "5.0.6"
"zustand": "5.0.10"
},
"devDependencies": {
"@next/bundle-analyzer": "^15.4.2",
"@tailwindcss/typography": "^0.5.16",
"@tauri-apps/cli": "2.9.4",
"@tauri-apps/cli": "2.10.1",
"@testing-library/dom": "^10.4.0",
"@testing-library/react": "^16.3.0",
"@tursodatabase/database": "0.6.0-pre.28",
"@types/cors": "^2.8.17",
"@types/cssbeautify": "^0.3.5",
"@types/lunr": "^2.3.7",
"@types/mocha": "^10.0.10",
"@types/node": "^22.15.31",
"@types/react": "18.3.12",
"@types/nunjucks": "^3.2.6",
"@types/react": "^19.0.0",
"@types/react-color": "^3.0.13",
"@types/react-dom": "18.3.1",
"@types/react-dom": "^19.0.0",
"@types/react-window": "^1.8.8",
"@types/semver": "^7.7.0",
"@types/tinycolor2": "^1.4.6",
"@types/uuid": "^10.0.0",
"@typescript-eslint/eslint-plugin": "^8.48.0",
"@typescript-eslint/parser": "^8.48.0",
"@vitejs/plugin-react": "^4.7.0",
"@types/ws": "^8.18.1",
"@typescript/native-preview": "7.0.0-dev.20260312.1",
"@vitejs/plugin-react": "^5.1.1",
"@vitejs/plugin-rsc": "^0.5.23",
"@vitest/browser-playwright": "^4.0.18",
"@vitest/browser-webdriverio": "^4.0.18",
"@vitest/coverage-v8": "^4.0.18",
"@wdio/cli": "^9.25.0",
"@wdio/globals": "^9.23.0",
"@wdio/local-runner": "^9.24.0",
"@wdio/mocha-framework": "^9.24.0",
"@wdio/spec-reporter": "^9.24.0",
"@wdio/types": "^9.24.0",
"autoprefixer": "^10.4.20",
"caniuse-lite": "^1.0.30001746",
"cpx2": "^8.0.0",
"daisyui": "^4.12.24",
"dotenv-cli": "^7.4.4",
"eslint": "^9.16.0",
"eslint-config-next": "16.0.0",
"eslint-plugin-jsx-a11y": "^6.10.2",
"i18next-scanner": "^4.6.0",
"jsdom": "^26.1.0",
"jsdom": "^28.1.0",
"mkdirp": "^3.0.1",
"node-env-run": "^4.0.2",
"playwright": "^1.58.2",
"postcss": "^8.4.49",
"postcss-cli": "^11.0.0",
"postcss-nested": "^7.0.2",
"raw-loader": "^4.0.2",
"tailwindcss": "^3.4.17",
"react-server-dom-webpack": "^19.2.5",
"serwist": "^9.3.0",
"tailwindcss": "^3.4.18",
"typescript": "^5.7.2",
"vinext": "^0.0.21",
"vite": "^7.3.3",
"vite-tsconfig-paths": "^5.1.4",
"vitest": "^3.2.4",
"wrangler": "^4.50.0"
}
"vitest": "^4.0.18",
"wrangler": "^4.85.0"
},
"browserslist": [
"chrome 92",
"edge 92",
"firefox 92",
"safari 15.2"
]
}
@@ -8,7 +8,15 @@
"components": [
{
"/": "/auth/*",
"comment": "Matches any URL whose path starts with /auth/"
"comment": "Matches OAuth callback paths"
},
{
"/": "/o/*",
"comment": "Matches annotation deeplinks"
},
{
"/": "/s/*",
"comment": "Matches book share links"
}
]
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

@@ -101,11 +101,9 @@
"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",
"Override Book Font": "تجاوز خط الكتاب",
"Apply to All Books": "تطبيق على جميع الكتب",
"Apply to This Book": "تطبيق على هذا الكتاب",
@@ -172,9 +170,6 @@
"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": "دعم المجتمع",
@@ -187,7 +182,6 @@
"RTL Direction": "الاتجاه من اليمين إلى اليسار",
"Maximum Column Height": "الارتفاع الأقصى للعمود",
"Maximum Column Width": "العرض الأقصى للعمود",
"Continuous Scroll": "التمرير المستمر",
"Fullscreen": "ملء الشاشة",
"No supported files found. Supported formats: {{formats}}": "لم يتم العثور على ملفات مدعومة. الصيغ المدعومة: {{formats}}",
"Drop to Import Books": "قم بالسحب والإسقاط لاستيراد الكتب",
@@ -311,7 +305,6 @@
"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}} دقيقة متبقية حتى نهاية الفصل",
@@ -349,15 +342,15 @@
"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}} صفحة في هذا الفصل",
"{{count}} pages left in chapter_zero": "<1>لم يتبق أي صفحات في هذا الفصل</1>",
"{{count}} pages left in chapter_one": "<1>تبقّت صفحة واحدة في هذا الفصل</1>",
"{{count}} pages left in chapter_two": "<1>تبقّت صفحتان في هذا الفصل</1>",
"{{count}} pages left in chapter_few": "<1>تبقّت </1><0>{{count}}</0><1> صفحات في هذا الفصل</1>",
"{{count}} pages left in chapter_many": "<1>تبقّت </1><0>{{count}}</0><1> صفحة في هذا الفصل</1>",
"{{count}} pages left in chapter_other": "<1>تبقّى </1><0>{{count}}</0><1> صفحة في هذا الفصل</1>",
"Show Remaining Pages": "عرض الصفحات المتبقية",
"Source Han Serif CN VF": "Source Han Serif",
"Huiwen-mincho": "Huiwen Mincho",
"Source Han Serif CN": "Source Han Serif",
"Huiwen-MinchoGBK": "Huiwen Mincho",
"KingHwa_OldSong": "KingHwa Song",
"Manage Subscription": "إدارة الاشتراك",
"Coming Soon": "قريبًا",
@@ -482,14 +475,12 @@
"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": "استلام التغييرات فقط",
"Checksum Method": "طريقة التحقق من الصحة",
"File Content (recommended)": "محتوى الملف (موصى به)",
"File Name": "اسم الملف",
"Device Name": "اسم الجهاز",
"Connect to your KOReader Sync server.": "الاتصال بخادم مزامنة KOReader الخاص بك.",
@@ -500,10 +491,7 @@
"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}}%)",
@@ -618,11 +606,10 @@
"Size": "حجم",
"Cover": "غطاء",
"Contain": "احتواء",
"{{number}} pages left in chapter": "تبقّت {{number}} صفحات في هذا الفصل",
"{{number}} pages left in chapter": "<1>تبقّت </1><0>{{number}}</0><1> صفحات في هذا الفصل</1>",
"Device": "الجهاز",
"E-Ink Mode": "وضع الحبر الإلكتروني",
"Highlight Colors": "ألوان التمييز",
"Auto Screen Brightness": "سطوع الشاشة التلقائي",
"Pagination": "التقسيم إلى صفحات",
"Disable Double Tap": "تعطيل النقر المزدوج",
"Tap to Paginate": "اضغط للتقسيم إلى صفحات",
@@ -657,7 +644,6 @@
"Strikethrough": "يتوسطه خط",
"Squiggly": "متعرج",
"Outline": "مخطط",
"Save Current Color": "حفظ اللون الحالي",
"Quick Colors": "ألوان سريعة",
"Highlighter": "محدد النص",
"Save Book Cover": "حفظ غلاف الكتاب",
@@ -715,9 +701,7 @@
"Validating...": "جارٍ التحقق...",
"View All": "عرض الكل",
"Forward": "إلى الأمام",
"OPDS Catalog": "كتالوج OPDS",
"Home": "الصفحة الرئيسية",
"Library": "المكتبة",
"{{count}} items_zero": "{{count}} عناصر",
"{{count}} items_one": "{{count}} عنصر",
"{{count}} items_two": "{{count}} عنصران",
@@ -741,5 +725,792 @@
"Last": "الأخير",
"Cannot Load Page": "تعذر تحميل الصفحة",
"An error occurred": "حدث خطأ ما",
"Online Library": "المكتبة عبر الإنترنت"
"Online Library": "المكتبة عبر الإنترنت",
"URL must start with http:// or https://": "يجب أن يبدأ عنوان URL بـ http:// أو https://",
"Title, Author, Tag, etc...": "العنوان، المؤلف، العلامة، إلخ...",
"Query": "استعلام",
"Subject": "موضوع",
"Enter {{terms}}": "أدخل {{terms}}",
"No search results found": "لم يتم العثور على نتائج بحث",
"Failed to load OPDS feed: {{status}} {{statusText}}": "فشل في تحميل تغذية OPDS: {{status}} {{statusText}}",
"Search in {{title}}": "البحث في {{title}}",
"Manage Storage": "إدارة التخزين",
"Failed to load files": "فشل في تحميل الملفات",
"Deleted {{count}} file(s)_zero": "لم يتم حذف أي ملفات",
"Deleted {{count}} file(s)_one": "تم حذف ملف واحد",
"Deleted {{count}} file(s)_two": "تم حذف ملفين",
"Deleted {{count}} file(s)_few": "تم حذف {{count}} ملفات",
"Deleted {{count}} file(s)_many": "تم حذف {{count}} ملفًا",
"Deleted {{count}} file(s)_other": "تم حذف {{count}} ملف",
"Failed to delete {{count}} file(s)_zero": "فشل حذف أي ملفات",
"Failed to delete {{count}} file(s)_one": "فشل حذف ملف واحد",
"Failed to delete {{count}} file(s)_two": "فشل حذف ملفين",
"Failed to delete {{count}} file(s)_few": "فشل حذف {{count}} ملفات",
"Failed to delete {{count}} file(s)_many": "فشل حذف {{count}} ملفًا",
"Failed to delete {{count}} file(s)_other": "فشل حذف {{count}} ملف",
"Failed to delete files": "فشل حذف الملفات",
"Total Files": "إجمالي الملفات",
"Total Size": "إجمالي الحجم",
"Quota": "الحصة",
"Used": "المستخدم",
"Files": "الملفات",
"Search files...": "ابحث في الملفات...",
"Newest First": "الأحدث أولاً",
"Oldest First": "الأقدم أولاً",
"Largest First": "الأكبر أولاً",
"Smallest First": "الأصغر أولاً",
"Name A-Z": "الاسم من أ إلى ي",
"Name Z-A": "الاسم من ي إلى أ",
"{{count}} selected_zero": "لا يوجد عناصر محددة",
"{{count}} selected_one": "عنصر واحد محدد",
"{{count}} selected_two": "عنصران محددان",
"{{count}} selected_few": "{{count}} عناصر محددة",
"{{count}} selected_many": "{{count}} عنصرًا محددًا",
"{{count}} selected_other": "{{count}} عنصر محدد",
"Delete Selected": "حذف المحدد",
"Created": "تاريخ الإنشاء",
"No files found": "لا توجد ملفات",
"No files uploaded yet": "لم يتم رفع أي ملفات بعد",
"files": "الملفات",
"Page {{current}} of {{total}}": "الصفحة {{current}} من {{total}}",
"Are you sure to delete {{count}} selected file(s)?_zero": "هل أنت متأكد من حذف العناصر المحددة؟ (لا توجد عناصر)",
"Are you sure to delete {{count}} selected file(s)?_one": "هل أنت متأكد من حذف ملف واحد محدد؟",
"Are you sure to delete {{count}} selected file(s)?_two": "هل أنت متأكد من حذف ملفين محددين؟",
"Are you sure to delete {{count}} selected file(s)?_few": "هل أنت متأكد من حذف {{count}} ملفات محددة؟",
"Are you sure to delete {{count}} selected file(s)?_many": "هل أنت متأكد من حذف {{count}} ملفًا محددًا؟",
"Are you sure to delete {{count}} selected file(s)?_other": "هل أنت متأكد من حذف {{count}} ملف محدد؟",
"Cloud Storage Usage": "استخدام التخزين السحابي",
"Rename Group": "إعادة تسمية المجموعة",
"From Directory": "من الدليل",
"Successfully imported {{count}} book(s)_zero": "لم يتم استيراد أي كتب",
"Successfully imported {{count}} book(s)_one": "تم استيراد كتاب واحد",
"Successfully imported {{count}} book(s)_two": "تم استيراد كتابين",
"Successfully imported {{count}} book(s)_few": "تم استيراد {{count}} كتب",
"Successfully imported {{count}} book(s)_many": "تم استيراد {{count}} كتابًا",
"Successfully imported {{count}} book(s)_other": "تم استيراد {{count}} كتاب",
"Count": "العدد",
"Start Page": "الصفحة الأولى",
"Search in OPDS Catalog...": "البحث في كتالوج OPDS...",
"Please log in to use advanced TTS features": "يرجى تسجيل الدخول لاستخدام ميزات تحويل النص إلى كلام المتقدمة.",
"Word limit of 30 words exceeded.": "تم تجاوز حد الكلمات البالغ 30 كلمة.",
"Proofread": "التدقيق اللغوي",
"Current selection": "النص المحدد الحالي",
"All occurrences in this book": "جميع الحالات في هذا الكتاب",
"All occurrences in your library": "جميع الحالات في مكتبتك",
"Selected text:": "النص المحدد:",
"Replace with:": "استبدال بـ:",
"Enter text...": "أدخل النص...",
"Case sensitive:": "حساس لحالة الأحرف:",
"Scope:": "النطاق:",
"Selection": "التحديد",
"Library": "المكتبة",
"Yes": "نعم",
"No": "لا",
"Proofread Replacement Rules": "قواعد استبدال التدقيق اللغوي",
"Selected Text Rules": "قواعد النص المحدد",
"No selected text replacement rules": "لا توجد قواعد استبدال نص محدد",
"Book Specific Rules": "قواعد خاصة بالكتاب",
"No book-level replacement rules": "لا توجد قواعد استبدال على مستوى الكتاب",
"Disable Quick Action": "تعطيل الإجراء السريع",
"Enable Quick Action on Selection": "تمكين الإجراء السريع عند التحديد",
"None": "لا شيء",
"Annotation Tools": "أدوات التعليق",
"Enable Quick Actions": "تمكين الإجراءات السريعة",
"Quick Action": "الإجراء السريع",
"Copy to Notebook": "نسخ إلى الدفتر",
"Copy text after selection": "نسخ النص بعد التحديد",
"Highlight text after selection": "تمييز النص بعد التحديد",
"Annotate text after selection": "تعليق على النص بعد التحديد",
"Search text after selection": "البحث في النص بعد التحديد",
"Look up text in dictionary after selection": "البحث عن النص في القاموس بعد التحديد",
"Translate text after selection": "ترجمة النص بعد التحديد",
"Read text aloud after selection": "قراءة النص بصوت عالٍ بعد التحديد",
"Proofread text after selection": "تدقيق النص بعد التحديد",
"{{activeCount}} active, {{pendingCount}} pending": "{{activeCount}} نشط، {{pendingCount}} قيد الانتظار",
"{{failedCount}} failed": "{{failedCount}} فشل",
"Waiting...": "جارٍ الانتظار...",
"Failed": "فشل",
"Completed": "مكتمل",
"Cancelled": "ملغى",
"Retry": "إعادة المحاولة",
"Active": "نشط",
"Transfer Queue": "قائمة انتظار النقل",
"Upload All": "رفع الكل",
"Download All": "تنزيل الكل",
"Resume Transfers": "استئناف النقل",
"Pause Transfers": "إيقاف النقل مؤقتًا",
"Pending": "قيد الانتظار",
"No transfers": "لا توجد عمليات نقل",
"Retry All": "إعادة محاولة الكل",
"Clear Completed": "مسح المكتمل",
"Clear Failed": "مسح الفاشل",
"Upload queued: {{title}}": "الرفع في قائمة الانتظار: {{title}}",
"Download queued: {{title}}": "التنزيل في قائمة الانتظار: {{title}}",
"Book not found in library": "الكتاب غير موجود في المكتبة",
"Unknown error": "خطأ غير معروف",
"Please log in to continue": "يرجى تسجيل الدخول للمتابعة",
"Cloud File Transfers": "نقل الملفات السحابية",
"Show Search Results": "عرض نتائج البحث",
"Search results for '{{term}}'": "نتائج البحث عن «{{term}}»",
"Close Search": "إغلاق البحث",
"Previous Result": "النتيجة السابقة",
"Next Result": "النتيجة التالية",
"Bookmarks": "الإشارات المرجعية",
"Annotations": "التعليقات التوضيحية",
"Show Results": "عرض النتائج",
"Clear search": "مسح البحث",
"Clear search history": "مسح سجل البحث",
"Tap to Toggle Footer": "انقر لتبديل التذييل",
"Show Current Time": "عرض الوقت الحالي",
"Use 24 Hour Clock": "استخدام نظام 24 ساعة",
"Show Current Battery Status": "عرض حالة البطارية الحالية",
"Exported successfully": "تم التصدير بنجاح",
"Book exported successfully.": "تم تصدير الكتاب بنجاح.",
"Failed to export the book.": "فشل تصدير الكتاب.",
"Export Book": "تصدير الكتاب",
"Whole word:": "كلمة كاملة:",
"Date Published": "تاريخ النشر",
"Only for TTS:": "فقط لـ TTS:",
"Uploaded": "تم الرفع",
"Downloaded": "تم التنزيل",
"Deleted": "تم الحذف",
"Note:": "ملاحظة:",
"Time:": "الوقت:",
"Format Options": "خيارات التنسيق",
"Export Date": "تاريخ التصدير",
"Chapter Titles": "عناوين الفصول",
"Chapter Separator": "فاصل الفصول",
"Highlights": "التمييزات",
"Note Date": "تاريخ الملاحظة",
"Advanced": "متقدم",
"Hide": "إخفاء",
"Show": "إظهار",
"Use Custom Template": "استخدام قالب مخصص",
"Export Template": "قالب التصدير",
"Template Syntax:": "بناء جملة القالب:",
"Insert value": "إدراج قيمة",
"Format date (locale)": "تنسيق التاريخ (محلي)",
"Format date (custom)": "تنسيق التاريخ (مخصص)",
"Conditional": "شرطي",
"Loop": "حلقة",
"Available Variables:": "المتغيرات المتاحة:",
"Book title": "عنوان الكتاب",
"Book author": "مؤلف الكتاب",
"Export date": "تاريخ التصدير",
"Array of chapters": "مصفوفة الفصول",
"Chapter title": "عنوان الفصل",
"Array of annotations": "مصفوفة التعليقات",
"Highlighted text": "النص المميز",
"Annotation note": "ملاحظة التعليق",
"Date Format Tokens:": "رموز تنسيق التاريخ:",
"Year (4 digits)": "السنة (4 أرقام)",
"Month (01-12)": "الشهر (01-12)",
"Day (01-31)": "اليوم (01-31)",
"Hour (00-23)": "الساعة (00-23)",
"Minute (00-59)": "الدقيقة (00-59)",
"Second (00-59)": "الثانية (00-59)",
"Show Source": "إظهار المصدر",
"No content to preview": "لا يوجد محتوى للمعاينة",
"Export": "تصدير",
"Set Timeout": "تعيين المهلة",
"Select Voice": "اختر الصوت",
"Toggle Sticky Bottom TTS Bar": "تبديل شريط TTS الثابت",
"Display what I'm reading on Discord": "عرض ما أقرأه على Discord",
"Show on Discord": "عرض على Discord",
"Instant {{action}}": "{{action}} فوري",
"Instant {{action}} Disabled": "تم تعطيل {{action}} فوري",
"Annotation": "التعليق",
"Reset Template": "إعادة تعيين القالب",
"Annotation style": "نمط التعليق",
"Annotation color": "لون التعليق",
"Annotation time": "وقت التعليق",
"AI": "الذكاء الاصطناعي",
"Are you sure you want to re-index this book?": "هل أنت متأكد من إعادة فهرسة هذا الكتاب؟",
"Enable AI in Settings": "تفعيل الذكاء الاصطناعي في الإعدادات",
"Index This Book": "فهرسة هذا الكتاب",
"Enable AI search and chat for this book": "تفعيل البحث والدردشة بالذكاء الاصطناعي لهذا الكتاب",
"Start Indexing": "بدء الفهرسة",
"Indexing book...": "جاري فهرسة الكتاب...",
"Preparing...": "جاري التحضير...",
"Delete this conversation?": "حذف هذه المحادثة؟",
"No conversations yet": "لا توجد محادثات بعد",
"Start a new chat to ask questions about this book": "ابدأ محادثة جديدة لطرح أسئلة حول هذا الكتاب",
"Rename": "إعادة التسمية",
"New Chat": "محادثة جديدة",
"Chat": "محادثة",
"Please enter a model ID": "الرجاء إدخال معرف النموذج",
"Model not available or invalid": "النموذج غير متاح أو غير صالح",
"Failed to validate model": "فشل التحقق من النموذج",
"Couldn't connect to Ollama. Is it running?": "تعذر الاتصال بـ Ollama. هل يعمل؟",
"Invalid API key or connection failed": "مفتاح API غير صالح أو فشل الاتصال",
"Connection failed": "فشل الاتصال",
"AI Assistant": "مساعد الذكاء الاصطناعي",
"Enable AI Assistant": "تفعيل مساعد الذكاء الاصطناعي",
"Provider": "المزود",
"Ollama (Local)": "Ollama (محلي)",
"AI Gateway (Cloud)": "بوابة الذكاء الاصطناعي (سحابي)",
"Ollama Configuration": "إعدادات Ollama",
"Refresh Models": "تحديث النماذج",
"AI Model": "نموذج الذكاء الاصطناعي",
"No models detected": "لم يتم اكتشاف نماذج",
"AI Gateway Configuration": "إعدادات بوابة الذكاء الاصطناعي",
"Choose from a selection of high-quality, economical AI models. You can also bring your own model by selecting \"Custom Model\" below.": "اختر من مجموعة من نماذج الذكاء الاصطناعي عالية الجودة والاقتصادية. يمكنك أيضًا استخدام نموذجك الخاص بتحديد \"نموذج مخصص\" أدناه.",
"API Key": "مفتاح API",
"Get Key": "احصل على المفتاح",
"Model": "النموذج",
"Custom Model...": "نموذج مخصص...",
"Custom Model ID": "معرف النموذج المخصص",
"Validate": "تحقق",
"Model available": "النموذج متاح",
"Connection": "الاتصال",
"Test Connection": "اختبار الاتصال",
"Connected": "متصل",
"Custom Colors": "ألوان مخصصة",
"Color E-Ink Mode": "وضع الحبر الإلكتروني الملون",
"Reading Ruler": "مسطرة القراءة",
"Enable Reading Ruler": "تفعيل مسطرة القراءة",
"Lines to Highlight": "الأسطر المراد تمييزها",
"Ruler Color": "لون المسطرة",
"Command Palette": "لوحة الأوامر",
"Search settings and actions...": "البحث في الإعدادات والإجراءات...",
"No results found for": "لم يتم العثور على نتائج لـ",
"Type to search settings and actions": "اكتب للبحث في الإعدادات والإجراءات",
"Recent": "الأخير",
"navigate": "التنقل",
"select": "اختيار",
"close": "إغلاق",
"Search Settings": "البحث في الإعدادات",
"Page Margins": "هوامش الصفحة",
"AI Provider": "مزود الذكاء الاصطناعي",
"Ollama URL": "رابط Ollama",
"Ollama Model": "نموذج Ollama",
"AI Gateway Model": "نموذج بوابة الذكاء الاصطناعي",
"Actions": "الإجراءات",
"Navigation": "التنقل",
"Set status for {{count}} book(s)_one": "تعيين الحالة لـ {{count}} كتاب",
"Set status for {{count}} book(s)_other": "تعيين الحالة لـ {{count}} كتب",
"Mark as Unread": "تحديد كغير مقروء",
"Mark as Finished": "تحديد كمنتهى",
"Finished": "منتهى",
"Unread": "غير مقروء",
"Clear Status": "مسح الحالة",
"Status": "الحالة",
"Set status for {{count}} book(s)_zero": "تعيين الحالة للكتب",
"Set status for {{count}} book(s)_two": "تعيين الحالة للكتابين",
"Set status for {{count}} book(s)_few": "تعيين الحالة لـ {{count}} كتب",
"Set status for {{count}} book(s)_many": "تعيين الحالة لـ {{count}} كتاباً",
"Loading": "جارٍ التحميل...",
"Exit Paragraph Mode": "الخروج من وضع الفقرة",
"Paragraph Mode": "وضع الفقرة",
"Embedding Model": "نموذج التضمين",
"{{count}} book(s) synced_zero": "لم تتم مزامنة أي كتب",
"{{count}} book(s) synced_one": "تمت مزامنة كتاب واحد",
"{{count}} book(s) synced_two": "تمت مزامنة كتابين",
"{{count}} book(s) synced_few": "تمت مزامنة {{count}} كتب",
"{{count}} book(s) synced_many": "تمت مزامنة {{count}} كتاباً",
"{{count}} book(s) synced_other": "تمت مزامنة {{count}} كتاب",
"Unable to start RSVP": "تعذر بدء RSVP",
"RSVP not supported for PDF": "RSVP غير مدعوم لملفات PDF",
"Select Chapter": "اختر الفصل",
"Context": "السياق",
"Ready": "جاهز",
"Chapter Progress": "تقدم الفصل",
"words": "كلمات",
"{{time}} left": "متبقي {{time}}",
"Reading progress": "تقدم القراءة",
"Skip back 15 words": "تخطي للخلف 15 كلمة",
"Back 15 words (Shift+Left)": "للخلف 15 كلمة (Shift+اليسار)",
"Pause (Space)": "إيقاف مؤقت (المسافة)",
"Play (Space)": "تشغيل (المسافة)",
"Skip forward 15 words": "تخطي للأمام 15 كلمة",
"Forward 15 words (Shift+Right)": "للأمام 15 كلمة (Shift+اليمين)",
"Decrease speed": "تقليل السرعة",
"Slower (Left/Down)": "أبطأ (اليسار/الأسفل)",
"Increase speed": "زيادة السرعة",
"Faster (Right/Up)": "أسرع (اليمين/الأعلى)",
"Start RSVP Reading": "بدء قراءة RSVP",
"Choose where to start reading": "اختر من أين تبدأ القراءة",
"From Chapter Start": "من بداية الفصل",
"Start reading from the beginning of the chapter": "بدء القراءة من بداية الفصل",
"Resume": "استئناف",
"Continue from where you left off": "المتابعة من حيث توقف",
"From Current Page": "من الصفحة الحالية",
"Start from where you are currently reading": "البدء من حيث تقرأ حالياً",
"From Selection": "من التحديد",
"Speed Reading Mode": "وضع القراءة السريعة",
"Scroll left": "تمرير لليسار",
"Scroll right": "تمرير لليمين",
"Library Sync Progress": "تقدم تزامن المكتبة",
"Back to library": "العودة إلى المكتبة",
"Group by...": "تجميع حسب...",
"Export as Plain Text": "تصدير كنص بسيط",
"Export as Markdown": "تصدير بصيغة Markdown",
"Show Page Navigation Buttons": "أزرار التنقل",
"Page {{number}}": "صفحة {{number}}",
"highlight": "تمييز",
"underline": "تسطير",
"squiggly": "متعرج",
"red": "أحمر",
"violet": "بنفسجي",
"blue": "أزرق",
"green": "أخضر",
"yellow": "أصفر",
"Select {{style}} style": "اختر نمط {{style}}",
"Select {{color}} color": "اختر لون {{color}}",
"Close Book": "إغلاق الكتاب",
"Speed Reading": "القراءة السريعة",
"Close Speed Reading": "إغلاق القراءة السريعة",
"Authors": "المؤلفون",
"Books": "الكتب",
"Groups": "المجموعات",
"Back to TTS Location": "العودة إلى موقع القراءة الآلية",
"Metadata": "بيانات وصفية",
"Image viewer": "عارض الصور",
"Previous Image": "الصورة السابقة",
"Next Image": "الصورة التالية",
"Zoomed": "مكبّر",
"Zoom level": "مستوى التكبير",
"Table viewer": "عارض الجداول",
"Unable to connect to Readwise. Please check your network connection.": "تعذر الاتصال بـ Readwise. يرجى التحقق من اتصال الشبكة لديك.",
"Invalid Readwise access token": "رمز وصول Readwise غير صالح",
"Disconnected from Readwise": "تم قطع الاتصال بـ Readwise",
"Never": "أبداً",
"Sync Enabled": "تم تمكين المزامنة",
"Disconnect": "قطع الاتصال",
"Connect your Readwise account to sync highlights.": "قم بتوصيل حساب Readwise الخاص بك لمزامنة التمييزات.",
"Get your access token at": "احصل على رمز الوصول الخاص بك من",
"Access Token": "رمز الوصول",
"Paste your Readwise access token": "الصق رمز وصول Readwise الخاص بك",
"Readwise Sync": "مزامنة Readwise",
"Push Highlights": "إرسال التمييزات",
"Highlights synced to Readwise": "تمت مزامنة التمييزات مع Readwise",
"Readwise sync failed: no internet connection": "فشلت مزامنة Readwise: لا يوجد اتصال بالإنترنت",
"Readwise sync failed: {{error}}": "فشلت مزامنة Readwise: {{error}}",
"System Screen Brightness": "سطوع شاشة النظام",
"Page:": "صفحة:",
"Page: {{number}}": "صفحة: {{number}}",
"Annotation page number": "رقم صفحة التعליق",
"Translating...": "جارٍ الترجمة...",
"Show Battery Percentage": "عرض نسبة البطارية",
"Hide Scrollbar": "إخفاء شريط التمرير",
"Skip to last reading position": "انتقل إلى آخر موضع قراءة",
"Show context": "إظهار السياق",
"Hide context": "إخفاء السياق",
"Decrease font size": "تصغير حجم الخط",
"Increase font size": "تكبير حجم الخط",
"Focus": "تركيز",
"Theme color": "لون السمة",
"Import failed": "فشل الاستيراد",
"Available Formatters:": "المُنسّقات المتاحة:",
"Format date": "تنسيق التاريخ",
"Markdown block quote (> per line)": "اقتباس Markdown (> لكل سطر)",
"Newlines to <br>": "أسطر جديدة إلى <br>",
"Change case": "تغيير حالة الأحرف",
"Trim whitespace": "إزالة المسافات",
"Truncate to n characters": "اقتطاع إلى n حرف",
"Replace text": "استبدال النص",
"Fallback value": "القيمة الاحتياطية",
"Get length": "الحصول على الطول",
"First/last element": "العنصر الأول/الأخير",
"Join array": "دمج المصفوفة",
"Backup failed: {{error}}": "فشل النسخ الاحتياطي: {{error}}",
"Select Backup": "اختر نسخة احتياطية",
"Restore failed: {{error}}": "فشل الاستعادة: {{error}}",
"Backup & Restore": "النسخ الاحتياطي والاستعادة",
"Create a backup of your library or restore from a previous backup. Restoring will merge with your current library.": "أنشئ نسخة احتياطية من مكتبتك أو استعد من نسخة احتياطية سابقة. ستدمج الاستعادة مع مكتبتك الحالية.",
"Backup Library": "نسخ المكتبة احتياطيًا",
"Restore Library": "استعادة المكتبة",
"Creating backup...": "جارٍ إنشاء النسخة الاحتياطية...",
"Restoring library...": "جارٍ استعادة المكتبة...",
"{{current}} of {{total}} items": "{{current}} من {{total}} عنصر",
"Backup completed successfully!": "تم النسخ الاحتياطي بنجاح!",
"Restore completed successfully!": "تمت الاستعادة بنجاح!",
"Your library has been saved to the selected location.": "تم حفظ مكتبتك في الموقع المحدد.",
"{{added}} books added, {{updated}} books updated.": "تمت إضافة {{added}} كتب، وتحديث {{updated}} كتب.",
"Operation failed": "فشلت العملية",
"Loading library...": "جارٍ تحميل المكتبة...",
"{{count}} books refreshed_zero": "لم يتم تحديث أي كتاب",
"{{count}} books refreshed_one": "تم تحديث كتاب واحد",
"{{count}} books refreshed_two": "تم تحديث كتابين",
"{{count}} books refreshed_few": "تم تحديث {{count}} كتب",
"{{count}} books refreshed_many": "تم تحديث {{count}} كتابًا",
"{{count}} books refreshed_other": "تم تحديث {{count}} كتاب",
"Failed to refresh metadata": "فشل تحديث البيانات الوصفية",
"Refresh Metadata": "تحديث البيانات الوصفية",
"Keyboard Shortcuts": "اختصارات لوحة المفاتيح",
"View all keyboard shortcuts": "عرض جميع اختصارات لوحة المفاتيح",
"Switch Sidebar Tab": "تبديل علامة تبويب الشريط الجانبي",
"Toggle Notebook": "إظهار/إخفاء دفتر الملاحظات",
"Search in Book": "البحث في الكتاب",
"Toggle Scroll Mode": "تبديل وضع التمرير",
"Toggle Select Mode": "تبديل وضع التحديد",
"Toggle Bookmark": "تبديل الإشارة المرجعية",
"Toggle Text to Speech": "تبديل تحويل النص إلى كلام",
"Play / Pause TTS": "تشغيل / إيقاف القراءة الصوتية",
"Toggle Paragraph Mode": "تبديل وضع الفقرة",
"Highlight Selection": "تمييز التحديد",
"Underline Selection": "وضع خط تحت التحديد",
"Annotate Selection": "إضافة تعليق على التحديد",
"Search Selection": "البحث عن التحديد",
"Copy Selection": "نسخ التحديد",
"Translate Selection": "ترجمة التحديد",
"Dictionary Lookup": "البحث في القاموس",
"Read Aloud Selection": "قراءة التحديد بصوت عالٍ",
"Proofread Selection": "تدقيق التحديد",
"Open Settings": "فتح الإعدادات",
"Open Command Palette": "فتح لوحة الأوامر",
"Show Keyboard Shortcuts": "عرض اختصارات لوحة المفاتيح",
"Open Books": "فتح الكتب",
"Toggle Fullscreen": "تبديل ملء الشاشة",
"Close Window": "إغلاق النافذة",
"Quit App": "إنهاء التطبيق",
"Go Left / Previous Page": "الانتقال لليسار / الصفحة السابقة",
"Go Right / Next Page": "الانتقال لليمين / الصفحة التالية",
"Go Up": "الانتقال لأعلى",
"Go Down": "الانتقال لأسفل",
"Previous Chapter": "الفصل السابق",
"Next Chapter": "الفصل التالي",
"Scroll Half Page Down": "تمرير نصف صفحة لأسفل",
"Scroll Half Page Up": "تمرير نصف صفحة لأعلى",
"Save Note": "حفظ الملاحظة",
"Single Section Scroll": "التمرير بقسم واحد",
"General": "عام",
"Text to Speech": "تحويل النص إلى كلام",
"Zoom": "تكبير",
"Window": "نافذة",
"Your Bookshelf": "رف الكتب الخاص بك",
"TTS": "تحويل النص إلى كلام",
"Media Info": "معلومات الوسائط",
"Update Frequency": "تردد التحديث",
"Every Sentence": "كل جملة",
"Every Paragraph": "كل فقرة",
"Every Chapter": "كل فصل",
"TTS Media Info Update Frequency": "تردد تحديث معلومات وسائط TTS",
"Select reading speed": "اختر سرعة القراءة",
"Drag to seek": "اسحب للتقديم",
"Punctuation Delay": "تأخير علامات الترقيم",
"Please confirm that this OPDS connection will be proxied through Readest servers on the web app before continuing.": "يرجى التأكيد على أن اتصال OPDS هذا سيتم توجيهه عبر خوادم Readest على تطبيق الويب قبل المتابعة.",
"Custom Headers (optional)": "رؤوس مخصصة (اختياري)",
"Add one header per line using \"Header-Name: value\".": "أضف رأسًا واحدًا لكل سطر باستخدام \"Header-Name: value\".",
"I understand this OPDS connection will be proxied through Readest servers on the web app. If I do not trust Readest with these credentials or headers, I should use the native app instead.": "أفهم أن اتصال OPDS هذا سيتم توجيهه عبر خوادم Readest على تطبيق الويب. إذا لم أثق بـ Readest فيما يتعلق ببيانات الاعتماد أو الرؤوس هذه، يجب أن أستخدم التطبيق الأصلي بدلاً من ذلك.",
"Unable to connect to Hardcover. Please check your network connection.": "تعذر الاتصال بـ Hardcover. يرجى التحقق من اتصال الشبكة.",
"Invalid Hardcover API token": "رمز API الخاص بـ Hardcover غير صالح",
"Disconnected from Hardcover": "تم قطع الاتصال بـ Hardcover",
"Connect your Hardcover account to sync reading progress and notes.": "اربط حسابك على Hardcover لمزامنة تقدم القراءة والملاحظات.",
"Get your API token from hardcover.app → Settings → API.": "احصل على رمز API من hardcover.app ← الإعدادات ← API.",
"API Token": "رمز API",
"Paste your Hardcover API token": "الصق رمز API الخاص بك في Hardcover",
"Hardcover Sync": "مزامنة Hardcover",
"Push Notes": "دفع الملاحظات",
"No annotations or excerpts to sync for this book.": "لا توجد تعليقات توضيحية أو مقتطفات لمزامنتها لهذا الكتاب.",
"Configure Hardcover in Settings first.": "قم بإعداد Hardcover في الإعدادات أولاً.",
"No new Hardcover note changes to sync.": "لا توجد تغييرات جديدة في ملاحظات Hardcover للمزامنة.",
"Hardcover synced: {{inserted}} new, {{updated}} updated, {{skipped}} unchanged": "تمت مزامنة Hardcover: {{inserted}} جديد، {{updated}} محدث، {{skipped}} بدون تغيير",
"Hardcover notes sync failed: {{error}}": "فشلت مزامنة ملاحظات Hardcover: {{error}}",
"Reading progress synced to Hardcover": "تمت مزامنة تقدم القراءة إلى Hardcover",
"Hardcover progress sync failed: {{error}}": "فشلت مزامنة تقدم Hardcover: {{error}}",
"ISBN": "ISBN",
"Keep existing source identifier": "الإبقاء على معرف المصدر الحالي",
"Toggle Toolbar": "تبديل شريط الأدوات",
"Split Hyphens": "تقسيم الواصلات",
"Apply Theme Colors to PDF": "تطبيق ألوان السمة على PDF",
"Name": "الاسم",
"Remove": "إزالة",
"Edit OPDS Catalog": "تعديل كتالوج OPDS",
"Save Changes": "حفظ التغييرات",
"Use Book Layout": "استخدام تخطيط الكتاب",
"End of this section. Continue to the next.": "نهاية هذا القسم. تابع إلى القسم التالي.",
"Auto-download": "تنزيل تلقائي",
"Auto-download new items": "تنزيل العناصر الجديدة تلقائياً",
"Automatically download new publications when the app syncs": "تنزيل المنشورات الجديدة تلقائياً عند مزامنة التطبيق",
"{{count}} new item(s) downloaded from OPDS_zero": "لم يتم تنزيل أي عناصر جديدة من OPDS",
"{{count}} new item(s) downloaded from OPDS_one": "تم تنزيل عنصر جديد من OPDS",
"{{count}} new item(s) downloaded from OPDS_two": "تم تنزيل عنصرين جديدين من OPDS",
"{{count}} new item(s) downloaded from OPDS_few": "تم تنزيل {{count}} عناصر جديدة من OPDS",
"{{count}} new item(s) downloaded from OPDS_many": "تم تنزيل {{count}} عنصراً جديداً من OPDS",
"{{count}} new item(s) downloaded from OPDS_other": "تم تنزيل {{count}} عنصر جديد من OPDS",
"Failed to sync {{count}} OPDS catalog(s)_zero": "فشلت مزامنة كتالوجات OPDS",
"Failed to sync {{count}} OPDS catalog(s)_one": "فشلت مزامنة كتالوج OPDS واحد",
"Failed to sync {{count}} OPDS catalog(s)_two": "فشلت مزامنة كتالوجي OPDS",
"Failed to sync {{count}} OPDS catalog(s)_few": "فشلت مزامنة {{count}} كتالوجات OPDS",
"Failed to sync {{count}} OPDS catalog(s)_many": "فشلت مزامنة {{count}} كتالوجاً من OPDS",
"Failed to sync {{count}} OPDS catalog(s)_other": "فشلت مزامنة {{count}} كتالوج OPDS",
"Last synced {{when}}": "آخر مزامنة {{when}}",
"Failed downloads": "التنزيلات الفاشلة",
"No failed downloads": "لا توجد تنزيلات فاشلة",
"Attempts: {{count}}_zero": "المحاولات: {{count}}",
"Attempts: {{count}}_one": "المحاولات: {{count}}",
"Attempts: {{count}}_two": "المحاولات: {{count}}",
"Attempts: {{count}}_few": "المحاولات: {{count}}",
"Attempts: {{count}}_many": "المحاولات: {{count}}",
"Attempts: {{count}}_other": "المحاولات: {{count}}",
"Skip": "تخطي",
"Skip all": "تخطي الكل",
"Retry all": "إعادة المحاولة للكل",
"{{count}} failed_zero": "{{count}} فشل",
"{{count}} failed_one": "{{count}} فشل",
"{{count}} failed_two": "{{count}} فشل",
"{{count}} failed_few": "{{count}} فشل",
"{{count}} failed_many": "{{count}} فشل",
"{{count}} failed_other": "{{count}} فشل",
"(none)": "(لا شيء)",
"Identifiers": "المعرفات",
"Read (Stream)": "قراءة (بث)",
"Failed to start stream": "فشل بدء البث",
"No dictionaries enabled": "لا توجد قواميس مفعلة",
"Enable a dictionary in Settings → Language → Dictionaries.": "فعّل قاموسًا من الإعدادات ← اللغة ← القواميس.",
"Wiktionary": "ويكاموس",
"Drag to reorder": "اسحب لإعادة الترتيب",
"Built-in": "مدمج",
"Bundle is missing on this device. Re-import to use it.": "الحزمة غير موجودة على هذا الجهاز. أعد استيرادها لاستخدامها.",
"This dictionary format is not supported.": "تنسيق هذا القاموس غير مدعوم.",
"MDict": "MDict",
"StarDict": "StarDict",
"Imported {{count}} dictionary_zero": "لم يتم استيراد أي قاموس",
"Imported {{count}} dictionary_one": "تم استيراد قاموس واحد ({{count}})",
"Imported {{count}} dictionary_two": "تم استيراد قاموسين",
"Imported {{count}} dictionary_few": "تم استيراد {{count}} قواميس",
"Imported {{count}} dictionary_many": "تم استيراد {{count}} قاموسًا",
"Imported {{count}} dictionary_other": "تم استيراد {{count}} قاموسًا",
"Skipped incomplete bundles: {{names}}": "تم تخطي الحزم غير المكتملة: {{names}}",
"Failed to import dictionary: {{message}}": "فشل استيراد القاموس: {{message}}",
"Dictionaries": "القواميس",
"Delete Dictionary": "حذف القاموس",
"Importing…": "جاري الاستيراد…",
"Import Dictionary": "استيراد قاموس",
"No dictionaries available.": "لا تتوفر قواميس.",
"StarDict bundles need .ifo, .idx, and .dict.dz files (.syn optional).": "تحتاج حزم StarDict إلى ملفات .ifo و‎.idx و‎.dict.dz (الـ‎.syn اختياري).",
"Select all the bundle files together when importing.": "حدد جميع ملفات الحزمة معًا عند الاستيراد.",
"Manage Dictionaries": "إدارة القواميس",
"Select Dictionary Files": "اختر ملفات القاموس",
"Read on Wikipedia →": "اقرأ على ويكيبيديا ←",
"This link can't be opened": "تعذر فتح هذا الرابط",
"The annotation link is missing required information. The original link may have been truncated.": "تفتقد إلى معلومات مطلوبة في رابط التعليق. ربما تم اقتطاع الرابط الأصلي.",
"Go to Readest": "الانتقال إلى Readest",
"Open-source ebook reader for everyone, on every device.": "قارئ كتب إلكتروني مفتوح المصدر للجميع وعلى كل جهاز.",
"Open in Readest": "فتح في Readest",
"If Readest didn't open automatically, choose an option below:": "إذا لم يفتح Readest تلقائيًا، فاختر أحد الخيارات أدناه:",
"Continue reading where you left off.": "تابع القراءة من حيث توقفت.",
"Readest logo": "شعار Readest",
"Opening Readest...": "جارٍ فتح Readest…",
"Open in Readest app": "فتح في تطبيق Readest",
"Continue in browser": "متابعة في المتصفح",
"Don't have Readest?": "ليس لديك Readest؟",
"Book not in your library": "الكتاب غير موجود في مكتبتك",
"Share Book": "مشاركة الكتاب",
"Could not create share link": "تعذر إنشاء رابط المشاركة",
"Link copied": "تم نسخ الرابط",
"Could not copy link": "تعذر نسخ الرابط",
"Share revoked": "تم إلغاء المشاركة",
"Could not revoke share": "تعذر إلغاء المشاركة",
"Uploading book…": "جاري رفع الكتاب…",
"Generating…": "جاري الإنشاء…",
"Generate share link": "إنشاء رابط مشاركة",
"Share URL": "رابط المشاركة",
"Copy link": "نسخ الرابط",
"Copied": "تم النسخ",
"Share via…": "مشاركة عبر…",
"Expires {{date}}": "تنتهي في {{date}}",
"Revoking…": "جاري الإلغاء…",
"Revoke share": "إلغاء المشاركة",
"Sign in to share books": "سجّل الدخول لمشاركة الكتب",
"Expiring soon": "تنتهي صلاحيته قريبًا",
"Missing share token": "رمز المشاركة مفقود",
"Could not add to your library": "تعذرت الإضافة إلى مكتبتك",
"This share link is no longer available": "لم يعد رابط المشاركة هذا متاحًا",
"The original link may have expired or been revoked.": "ربما انتهت صلاحية الرابط الأصلي أو تم إلغاؤه.",
"Get Readest": "احصل على Readest",
"Loading shared book…": "جاري تحميل الكتاب المشترك…",
"Adding…": "جاري الإضافة…",
"Add to my library": "إضافة إلى مكتبتي",
"Could not load your shares": "تعذر تحميل مشاركاتك",
"Revoked": "ملغى",
"Expired": "منتهي الصلاحية",
"Shared books": "الكتب المشتركة",
"You haven't shared any books yet": "لم تشارك أي كتب بعد",
"Open a book and tap Share to send it to a friend.": "افتح كتابًا واضغط على مشاركة لإرساله إلى صديق.",
"{{count}} active_zero": "لا توجد روابط نشطة",
"{{count}} active_one": "رابط نشط واحد",
"{{count}} active_two": "رابطان نشطان",
"{{count}} active_few": "{{count}} روابط نشطة",
"{{count}} active_many": "{{count}} رابطًا نشطًا",
"{{count}} active_other": "{{count}} رابط نشط",
"{{count}} downloads_zero": "لا توجد تنزيلات",
"{{count}} downloads_one": "تنزيل واحد",
"{{count}} downloads_two": "تنزيلان",
"{{count}} downloads_few": "{{count}} تنزيلات",
"{{count}} downloads_many": "{{count}} تنزيلًا",
"{{count}} downloads_other": "{{count}} تنزيل",
"starts at saved page": "يبدأ من الصفحة المحفوظة",
"More actions": "المزيد من الإجراءات",
"Loading…": "جاري التحميل…",
"Load more": "تحميل المزيد",
"Could not load shared book": "تعذر تحميل الكتاب المشترك",
"The share link is missing required information.": "يفتقد رابط المشاركة معلومات مطلوبة.",
"Please check your connection and try again.": "يرجى التحقق من اتصالك والمحاولة مرة أخرى.",
"Sign in to import shared books": "سجّل الدخول لاستيراد الكتب المشتركة",
"Already in your library": "موجود بالفعل في مكتبتك",
"Added to your library": "تمت الإضافة إلى مكتبتك",
"Could not import shared book": "تعذر استيراد الكتاب المشترك",
"Manage Shared Links": "إدارة روابط المشاركة",
"Expires in": "تنتهي خلال",
"{{count}} days_zero": "لا توجد أيام",
"{{count}} days_one": "يوم واحد",
"{{count}} days_two": "يومان",
"{{count}} days_few": "{{count}} أيام",
"{{count}} days_many": "{{count}} يومًا",
"{{count}} days_other": "{{count}} يوم",
"Expires in {{count}} days_zero": "تنتهي خلال أقل من يوم",
"Expires in {{count}} days_one": "تنتهي خلال يوم واحد",
"Expires in {{count}} days_two": "تنتهي خلال يومين",
"Expires in {{count}} days_few": "تنتهي خلال {{count}} أيام",
"Expires in {{count}} days_many": "تنتهي خلال {{count}} يومًا",
"Expires in {{count}} days_other": "تنتهي خلال {{count}} يوم",
"Expires in {{count}} hours_zero": "تنتهي خلال أقل من ساعة",
"Expires in {{count}} hours_one": "تنتهي خلال ساعة واحدة",
"Expires in {{count}} hours_two": "تنتهي خلال ساعتين",
"Expires in {{count}} hours_few": "تنتهي خلال {{count}} ساعات",
"Expires in {{count}} hours_many": "تنتهي خلال {{count}} ساعة",
"Expires in {{count}} hours_other": "تنتهي خلال {{count}} ساعة",
"Open in app": "فتح في التطبيق",
"Shared with you": "تمت مشاركته معك",
"Downloading… {{percent}}%": "جارٍ التنزيل… {{percent}}٪",
"Import progress": "تقدم الاستيراد",
"Share reading progress": "مشاركة تقدّم القراءة",
"Includes your reading progress": "يتضمن تقدّم قراءتك",
"Synced {{time}}": "تمت المزامنة {{time}}",
"Sync Info": "معلومات المزامنة",
"Last Synced": "آخر مزامنة",
"DICT": "DICT",
"Slob": "Slob",
"DICT bundles need a .index file and a .dict.dz file.": "تحتاج حزم DICT إلى ملف ‎.index وملف .dict.dz.",
"Slob bundles need a .slob file.": "تحتاج حزم Slob إلى ملف .slob.",
"URL template must start with http(s):// and contain %WORD%.": "يجب أن يبدأ قالب URL بـ http(s):// وأن يحتوي على %WORD%.",
"Web": "ويب",
"Add Web Search": "إضافة بحث ويب",
"Edit Web Search": "تعديل بحث الويب",
"e.g. Google": "مثال: Google",
"URL Template": "قالب URL",
"Use %WORD% where the looked-up word should appear.": "استخدم %WORD% حيث يجب أن تظهر الكلمة المبحوث عنها.",
"Open the search result in your browser:": "افتح نتيجة البحث في متصفحك:",
"Open in {{name}}": "افتح في {{name}}",
"Book Fingerprint": "بصمة الكتاب",
"Search the web": "البحث في الويب",
"App deeplink (readest://)": "رابط التطبيق (readest://)",
"Universal web link (https://)": "رابط ويب شامل (https://)",
"Name cannot be empty.": "لا يمكن أن يكون الاسم فارغًا.",
"Replaced {{count}} existing dictionary_zero": "لم يتم استبدال أي قاموس",
"Replaced {{count}} existing dictionary_one": "تم استبدال قاموس واحد",
"Replaced {{count}} existing dictionary_two": "تم استبدال قاموسين",
"Replaced {{count}} existing dictionary_few": "تم استبدال {{count}} قواميس",
"Replaced {{count}} existing dictionary_many": "تم استبدال {{count}} قاموسًا",
"Replaced {{count}} existing dictionary_other": "تم استبدال {{count}} قاموس",
"Cancel Edit": "إلغاء التعديل",
"Edit Dictionary": "تعديل القاموس",
"MDict bundles use .mdx files; companion .mdd and .css files are optional.": "تستخدم حزم MDict ملفات .mdx؛ ملفات .mdd و .css المرافقة اختيارية.",
"Dictionary name": "اسم القاموس",
"This audio can't play here — the dictionary uses an outdated format. Try one with Opus, MP3, or WAV audio.": "لا يمكن تشغيل هذا الصوت هنا — يستخدم القاموس تنسيقًا قديمًا. جرّب قاموسًا يستخدم صوت Opus أو MP3 أو WAV.",
"File uploaded: {{title}}": "تم تحميل الملف: {{title}}",
"File downloaded: {{title}}": "تم تنزيل الملف: {{title}}",
"Deleted cloud copy of the file: {{title}}": "تم حذف النسخة السحابية للملف: {{title}}",
"Failed to upload file: {{title}}": "فشل تحميل الملف: {{title}}",
"Failed to download file: {{title}}": "فشل تنزيل الملف: {{title}}",
"Failed to delete cloud copy of the file: {{title}}": "فشل في حذف النسخة السحابية للملف: {{title}}",
"{{percentage}}% used": "تم استخدام {{percentage}}٪",
"Set PIN…": "تعيين رمز PIN…",
"Require a 4-digit PIN to open Readest": "اطلب رمز PIN مكوّن من 4 أرقام لفتح Readest",
"Change PIN…": "تغيير رمز PIN…",
"Disable PIN…": "تعطيل رمز PIN…",
"Sync passphrase ready": "عبارة مرور المزامنة جاهزة",
"This permanently deletes the encrypted credentials we sync (e.g., OPDS catalog passwords) on every device. Local copies are preserved. You will need to re-enter the sync passphrase or set a new one. Continue?": "سيؤدي هذا إلى حذف بيانات الاعتماد المشفرة التي نقوم بمزامنتها (مثل كلمات مرور كتالوج OPDS) على كل جهاز بشكل دائم. سيتم الاحتفاظ بالنسخ المحلية. ستحتاج إلى إعادة إدخال عبارة مرور المزامنة أو تعيين عبارة جديدة. هل تريد المتابعة؟",
"Sync passphrase forgotten — all encrypted fields cleared": "تم نسيان عبارة مرور المزامنة — تم مسح جميع الحقول المشفرة",
"Sync passphrase": "عبارة مرور المزامنة",
"Set passphrase": "تعيين عبارة المرور",
"Forgot passphrase": "نسيت عبارة المرور",
"Incorrect PIN": "رمز PIN غير صحيح",
"App locked": "التطبيق مقفل",
"Enter your PIN": "أدخل رمز PIN",
"Readest is locked. Enter your 4-digit PIN to continue.": "Readest مقفل. أدخل رمز PIN المكوّن من 4 أرقام للمتابعة.",
"PIN code": "رمز PIN",
"Forgetting your PIN locks you out of this device. You'll need to clear the app's data to reset it.": "نسيان رمز PIN يمنعك من الوصول إلى هذا الجهاز. ستحتاج إلى مسح بيانات التطبيق لإعادة تعيينه.",
"Passphrase must be at least 8 characters": "يجب أن تتكون عبارة المرور من 8 أحرف على الأقل",
"Passphrases do not match": "عبارات المرور غير متطابقة",
"Set sync passphrase": "تعيين عبارة مرور المزامنة",
"Enter sync passphrase": "أدخل عبارة مرور المزامنة",
"A sync passphrase encrypts your sensitive fields (like OPDS catalog credentials) before they sync. We never see this passphrase. Pick something memorable — there is no recovery without it.": "تقوم عبارة مرور المزامنة بتشفير الحقول الحساسة (مثل بيانات اعتماد كتالوج OPDS) قبل مزامنتها. نحن لا نرى عبارة المرور هذه أبدًا. اختر شيئًا يمكن تذكره — لا توجد طريقة لاستعادتها بدونها.",
"Enter the sync passphrase you set on another device to decrypt your synced credentials.": "أدخل عبارة مرور المزامنة التي عيّنتها على جهاز آخر لفك تشفير بيانات الاعتماد المتزامنة.",
"Confirm passphrase": "تأكيد عبارة المرور",
"Unlock": "إلغاء القفل",
"Set PIN": "تعيين رمز PIN",
"Change PIN": "تغيير رمز PIN",
"Disable PIN": "تعطيل رمز PIN",
"Enter your current PIN, then choose a new 4-digit PIN.": "أدخل رمز PIN الحالي، ثم اختر رمز PIN جديد مكوّن من 4 أرقام.",
"Enter your current PIN to disable the app lock.": "أدخل رمز PIN الحالي لتعطيل قفل التطبيق.",
"PIN must be {{length}} digits": "يجب أن يتكون رمز PIN من {{length}} أرقام",
"PINs do not match": "رمزا PIN غير متطابقين",
"Current PIN": "رمز PIN الحالي",
"New PIN": "رمز PIN جديد",
"Confirm new PIN": "تأكيد رمز PIN الجديد",
"Manage Sync": "إدارة المزامنة",
"Imported book files and library metadata": "ملفات الكتب المستوردة وبيانات المكتبة",
"Last-read position, bookmarks, and per-book preferences": "آخر موضع قراءة والإشارات المرجعية وتفضيلات كل كتاب",
"Highlights and notes": "التظليلات والملاحظات",
"Fonts": "الخطوط",
"Custom font files": "ملفات الخطوط المخصصة",
"Backgrounds": "الخلفيات",
"Custom background textures": "أنسجة الخلفية المخصصة",
"OPDS catalogs": "كتالوجات OPDS",
"Saved catalog URLs and (encrypted) credentials": "روابط الكتالوجات المحفوظة وبيانات الاعتماد (المشفرة)",
"Wrong sync passphrase — synced credentials could not be decrypted": "عبارة مرور المزامنة غير صحيحة — تعذر فك تشفير بيانات الاعتماد المتزامنة",
"Sync passphrase data on the server was reset. Re-encrypting your credentials under the new passphrase…": "تمت إعادة تعيين بيانات عبارة مرور المزامنة على الخادم. جاري إعادة تشفير بيانات الاعتماد بعبارة المرور الجديدة…",
"Failed to decrypt synced credentials": "تعذر فك تشفير بيانات الاعتماد المتزامنة",
"Imported dictionary bundles and settings": "حزم القواميس المستوردة والإعدادات",
"Choose what syncs across your devices. Disabling a category stops this device from sending or receiving rows of that kind. Anything already on the server is left alone, re-enabling resumes from where you stopped.": "اختر ما تتم مزامنته عبر أجهزتك. تعطيل أي فئة يوقف هذا الجهاز عن إرسال أو استقبال السجلات من ذلك النوع. أي شيء موجود بالفعل على الخادم يُترك كما هو، وعند إعادة التفعيل تُستأنف المزامنة من حيث توقفت.",
"Data Sync": "مزامنة البيانات",
"Manage Fonts": "إدارة الخطوط",
"App settings": "إعدادات التطبيق",
"Theme, highlight colours, integrations (KOSync, Readwise, Hardcover), and dictionary order": "السمة وألوان التظليل والتكاملات (KOSync وReadwise وHardcover) وترتيب القواميس",
"Required while Dictionaries sync is enabled": "مطلوب أثناء تفعيل مزامنة القواميس",
"Resets in {{hours}} hr {{minutes}} min": "يُعاد الضبط خلال {{hours}} س {{minutes}} د",
"Pick a 4-digit PIN. You will need to enter it every time you open Readest. There is no way to recover a forgotten PIN. Clearing the app data is the only way to reset it.": "اختر رمز PIN مكوّنًا من 4 أرقام. ستحتاج إلى إدخاله في كل مرة تفتح فيها Readest. لا توجد طريقة لاستعادة رمز PIN المنسي. مسح بيانات التطبيق هو الطريقة الوحيدة لإعادة ضبطه.",
"Credentials": "بيانات الاعتماد",
"Tokens, usernames, and passwords for OPDS, KOReader, Hardcover, and Readwise. When disabled, credentials remain on this device only and are never uploaded.": "الرموز وأسماء المستخدمين وكلمات المرور لـ OPDS و KOReader و Hardcover و Readwise. عند التعطيل، تبقى بيانات الاعتماد على هذا الجهاز فقط ولا تُرفع أبدًا.",
"Sensitive synced fields are encrypted before upload. Set a passphrase now or later when encryption is needed.": "تُشفَّر الحقول الحساسة المتزامنة قبل الرفع. عيّن عبارة مرور الآن أو لاحقًا عند الحاجة إلى التشفير.",
"Saved to this account. You will be prompted for it when decrypting credentials.": "محفوظة في هذا الحساب. سيُطلب منك إدخالها عند فك تشفير بيانات الاعتماد.",
"Reading progress on this device differs from \"{{deviceName}}\".": "يختلف تقدم القراءة على هذا الجهاز عن «{{deviceName}}».",
"This device": "هذا الجهاز",
"Red": "أحمر",
"Yellow": "أصفر",
"Green": "أخضر",
"Blue": "أزرق",
"Violet": "بنفسجي",
"Edit color": "تعديل اللون",
"Add custom color": "إضافة لون مخصص",
"Add label": "إضافة تسمية",
"Choose color": "اختيار لون",
"Connected to Hardcover. Last synced {{time}}.": "متصل بـ Hardcover. آخر مزامنة {{time}}.",
"Integrations": "التكاملات",
"Hardcover": "Hardcover",
"Connected to Readwise. Last synced {{time}}.": "متصل بـ Readwise. آخر مزامنة {{time}}.",
"Readwise": "Readwise",
"Browse and download books from online catalogs.": "تصفح وتنزيل الكتب من الفهارس عبر الإنترنت.",
"Connected as {{user}}": "متصل باسم {{user}}",
"Not connected": "غير متصل",
"{{count}} catalog_zero": "لا توجد فهارس",
"{{count}} catalog_one": "فهرس واحد",
"{{count}} catalog_two": "فهرسان",
"{{count}} catalog_few": "{{count}} فهارس",
"{{count}} catalog_many": "{{count}} فهرسًا",
"{{count}} catalog_other": "{{count}} فهرس",
"No catalogs": "لا توجد فهارس",
"Connect Readest to external services for sync, highlights, and catalogs.": "اربط Readest بخدمات خارجية للمزامنة والإبرازات والفهارس.",
"Reading Sync": "مزامنة القراءة",
"Content Sources": "مصادر المحتوى",
"Scroll tabs": "تمرير علامات التبويب",
"Catalog actions": "إجراءات الفهرس",
"File Content": "محتوى الملف",
"Start your library": "ابدأ مكتبتك",
"Pick a book from your device to add it to your library.": "اختر كتابًا من جهازك لإضافته إلى مكتبتك.",
"Drop a book anywhere on this window, or pick one from your computer.": "أفلِت كتابًا في أي مكان على هذه النافذة، أو اختر واحدًا من حاسوبك.",
"Sign in to sync your library": "سجّل الدخول لمزامنة مكتبتك"
}
@@ -24,9 +24,6 @@
"Token": "টোকেন",
"Your OTP token": "আপনার ওটিপি টোকেন",
"Verify token": "টোকেন যাচাই করুন",
"Sign in with Google": "গুগল দিয়ে সাইন ইন",
"Sign in with Apple": "অ্যাপল দিয়ে সাইন ইন",
"Sign in with GitHub": "গিটহাব দিয়ে সাইন ইন",
"New Password": "নতুন পাসওয়ার্ড",
"Your new password": "আপনার নতুন পাসওয়ার্ড",
"Update password": "পাসওয়ার্ড আপডেট করুন",
@@ -120,7 +117,6 @@
"Failed to delete book: {{title}}": "বই মুছতে ব্যর্থ: {{title}}",
"Failed to delete cloud backup of the book: {{title}}": "বইয়ের ক্লাউড ব্যাকআপ মুছতে ব্যর্থ: {{title}}",
"Failed to delete local copy 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": "রিডেস্ট থেকে রপ্তানি করা হয়েছে",
@@ -158,13 +154,9 @@
"Small": "ছোট",
"Large": "বড়",
"Sync Conflict": "সিঙ্ক দ্বন্দ্ব",
"Sync reading progress from \"{{deviceName}}\"?": "\"{{deviceName}}\" থেকে পড়ার অগ্রগতি সিঙ্ক করবেন?",
"another device": "অন্য ডিভাইস",
"Local Progress": "স্থানীয় অগ্রগতি",
"Remote Progress": "দূরবর্তী অগ্রগতি",
"Failed to connect": "সংযোগ ব্যর্থ",
"Disconnected": "সংযোগ বিচ্ছিন্ন",
"KOReader Sync Settings": "KOReader সিঙ্ক সেটিংস",
"Sync as {{userDisplayName}}": "{{userDisplayName}} হিসেবে সিঙ্ক",
"Sync Server Connected": "সিঙ্ক সার্ভার সংযুক্ত",
"Sync Strategy": "সিঙ্ক কৌশল",
@@ -173,7 +165,6 @@
"Send changes only": "শুধু পরিবর্তন পাঠান",
"Receive changes only": "শুধু পরিবর্তন গ্রহণ করুন",
"Checksum Method": "চেকসাম পদ্ধতি",
"File Content (recommended)": "ফাইল কন্টেন্ট (সুপারিশকৃত)",
"File Name": "ফাইলের নাম",
"Device Name": "ডিভাইসের নাম",
"Connect to your KOReader Sync server.": "আপনার KOReader সিঙ্ক সার্ভারে সংযোগ করুন।",
@@ -189,8 +180,8 @@
"Add your notes here...": "এখানে আপনার নোট যোগ করুন...",
"Search notes and excerpts...": "নোট ও উদ্ধৃতি খুঁজুন...",
"{{time}} min left in chapter": "অধ্যায়ে {{time}} মিনিট বাকি",
"{{count}} pages left in chapter_one": "অধ্যায়ে ১ পৃষ্ঠা বাকি",
"{{count}} pages left in chapter_other": "অধ্যায়ে {{count}} পৃষ্ঠা বাকি",
"{{count}} pages left in chapter_one": "<1>অধ্যায়ে ১ পৃষ্ঠা বাকি</1>",
"{{count}} pages left in chapter_other": "<1>অধ্যায়ে </1><0>{{count}}</0><1> পৃষ্ঠা বাকি</1>",
"Theme Mode": "থিম মোড",
"Invert Image In Dark Mode": "ডার্ক মোডে ছবি উল্টান",
"Override Book Color": "বইয়ের রঙ পরিবর্তন",
@@ -202,7 +193,6 @@
"Auto": "অটো",
"Scroll": "স্ক্রল",
"Scrolled Mode": "স্ক্রল মোড",
"Continuous Scroll": "ক্রমাগত স্ক্রল",
"Overlap Pixels": "ওভারল্যাপ পিক্সেল",
"Disable Double Click": "ডাবল ক্লিক নিষ্ক্রিয়",
"Volume Keys for Page Flip": "পৃষ্ঠা উল্টানোর জন্য ভলিউম কী",
@@ -322,7 +312,6 @@
"Table of Contents": "সূচিপত্র",
"Sidebar": "সাইডবার",
"Disable Translation": "অনুবাদ নিষ্ক্রিয়",
"TTS not supported for PDF": "PDF এর জন্য TTS সমর্থিত নয়",
"TTS not supported for this document": "এই ডকুমেন্টের জন্য TTS সমর্থিত নয়",
"No Timeout": "কোন টাইমআউট নেই",
"{{value}} minute": "{{value}} মিনিট",
@@ -335,7 +324,6 @@
"{{engine}}: {{count}} voices_one": "{{engine}}: ১টি কণ্ঠস্বর",
"{{engine}}: {{count}} voices_other": "{{engine}}: {{count}}টি কণ্ঠস্বর",
"Sign in to Sync": "সিঙ্কের জন্য সাইন ইন",
"Synced at {{time}}": "{{time}} এ সিঙ্ক হয়েছে",
"Never synced": "কখনো সিঙ্ক হয়নি",
"Reading Progress Synced": "পড়ার অগ্রগতি সিঙ্ক হয়েছে",
"Page {{page}} of {{total}} ({{percentage}}%)": "{{total}} এর {{page}} পৃষ্ঠা ({{percentage}}%)",
@@ -488,8 +476,8 @@
"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",
"Source Han Serif CN": "Source Han Serif CN",
"Huiwen-MinchoGBK": "Huiwen Mincho",
"KingHwa_OldSong": "KingHwa_OldSong",
"Google Books": "গুগল বুকস",
"Open Library": "ওপেন লাইব্রেরি",
@@ -602,11 +590,10 @@
"Size": "আকার",
"Cover": "ঢাকা",
"Contain": "ধারণ",
"{{number}} pages left in chapter": "অধ্যায়ে {{number}} পৃষ্ঠা বাকি",
"{{number}} pages left in chapter": "<1>অধ্যায়ে </1><0>{{number}}</0><1> পৃষ্ঠা বাকি</1>",
"Device": "যন্ত্র",
"E-Ink Mode": "ই-ইঙ্ক মোড",
"Highlight Colors": "হাইলাইট রঙ",
"Auto Screen Brightness": "স্বয়ংক্রিয় স্ক্রিন উজ্জ্বলতা",
"Pagination": "পৃষ্ঠা বিন্যাস",
"Disable Double Tap": "ডাবল ট্যাপ অক্ষম করুন",
"Tap to Paginate": "পৃষ্ঠায় যেতে ট্যাপ করুন",
@@ -641,7 +628,6 @@
"Strikethrough": "কাটা লেখা",
"Squiggly": "ঢেউখেলানো দাগ",
"Outline": "বাহিরের রেখা",
"Save Current Color": "বর্তমান রঙ সংরক্ষণ করুন",
"Quick Colors": "দ্রুত রংসমূহ",
"Highlighter": "হাইলাইটার",
"Save Book Cover": "বইয়ের মলাট সংরক্ষণ করুন",
@@ -699,9 +685,7 @@
"Validating...": "যাচাই করা হচ্ছে...",
"View All": "সব দেখুন",
"Forward": "ফরোয়ার্ড",
"OPDS Catalog": "OPDS ক্যাটালগ",
"Home": "হোম",
"Library": "লাইব্রেরি",
"{{count}} items_one": "{{count}} আইটেম",
"{{count}} items_other": "{{count}} আইটেম",
"Download completed": "ডাউনলোড সম্পন্ন হয়েছে",
@@ -721,5 +705,712 @@
"Last": "শেষ",
"Cannot Load Page": "পৃষ্ঠা লোড করা যায়নি",
"An error occurred": "একটি ত্রুটি ঘটেছে",
"Online Library": "অনলাইন লাইব্রেরি"
"Online Library": "অনলাইন লাইব্রেরি",
"URL must start with http:// or https://": "URL অবশ্যই http:// বা https:// দিয়ে শুরু হতে হবে",
"Title, Author, Tag, etc...": "শিরোনাম, লেখক, ট্যাগ, ইত্যাদি...",
"Query": "কোয়েরি",
"Subject": "বিষয়",
"Enter {{terms}}": "{{terms}} লিখুন",
"No search results found": "কোনও অনুসন্ধান ফলাফল পাওয়া যায়নি",
"Failed to load OPDS feed: {{status}} {{statusText}}": "OPDS ফিড লোড করতে ব্যর্থ: {{status}} {{statusText}}",
"Search in {{title}}": "{{title}} এ অনুসন্ধান করুন",
"Manage Storage": "স্টোরেজ ম্যানেজমেন্ট",
"Failed to load files": "ফাইল লোড করতে ব্যর্থ",
"Deleted {{count}} file(s)_one": "{{count}}টি ফাইল মুছে ফেলা হয়েছে",
"Deleted {{count}} file(s)_other": "{{count}}টি ফাইল মুছে ফেলা হয়েছে",
"Failed to delete {{count}} file(s)_one": "{{count}}টি ফাইল মুছতে ব্যর্থ",
"Failed to delete {{count}} file(s)_other": "{{count}}টি ফাইল মুছতে ব্যর্থ",
"Failed to delete files": "ফাইল মুছতে ব্যর্থ",
"Total Files": "মোট ফাইল",
"Total Size": "মোট আকার",
"Quota": "কোটা",
"Used": "ব্যবহৃত",
"Files": "ফাইল",
"Search files...": "ফাইল খুঁজুন...",
"Newest First": "নতুন আগে",
"Oldest First": "পুরনো আগে",
"Largest First": "বড় আগে",
"Smallest First": "ছোট আগে",
"Name A-Z": "নাম A-Z",
"Name Z-A": "নাম Z-A",
"{{count}} selected_one": "{{count}}টি নির্বাচিত",
"{{count}} selected_other": "{{count}}টি নির্বাচিত",
"Delete Selected": "নির্বাচিত মুছুন",
"Created": "তৈরি হয়েছে",
"No files found": "কোনো ফাইল পাওয়া যায়নি",
"No files uploaded yet": "এখনও কোনো ফাইল আপলোড করা হয়নি",
"files": "ফাইল",
"Page {{current}} of {{total}}": "{{total}}টির মধ্যে {{current}} পৃষ্ঠা",
"Are you sure to delete {{count}} selected file(s)?_one": "আপনি কি নিশ্চিত যে {{count}}টি নির্বাচিত ফাইল মুছতে চান?",
"Are you sure to delete {{count}} selected file(s)?_other": "আপনি কি নিশ্চিত যে {{count}}টি নির্বাচিত ফাইল মুছতে চান?",
"Cloud Storage Usage": "ক্লাউড স্টোরেজ ব্যবহৃত",
"Rename Group": "গ্রুপের নাম পরিবর্তন করুন",
"From Directory": "ডিরেক্টরি থেকে",
"Successfully imported {{count}} book(s)_one": "সফলভাবে ১টি বই আমদানি করা হয়েছে",
"Successfully imported {{count}} book(s)_other": "সফলভাবে {{count}}টি বই আমদানি করা হয়েছে",
"Count": "গণনা",
"Start Page": "শুরু পৃষ্ঠা",
"Search in OPDS Catalog...": "OPDS ক্যাটালগে অনুসন্ধান করুন...",
"Please log in to use advanced TTS features": "উন্নত TTS বৈশিষ্ট্যগুলি ব্যবহার করতে লগইন করুন।",
"Word limit of 30 words exceeded.": "৩০ শব্দের শব্দসীমা অতিক্রম হয়েছে।",
"Proofread": "প্রুফরিড",
"Current selection": "বর্তমান নির্বাচন",
"All occurrences in this book": "এই বইয়ে সমস্ত ঘটনা",
"All occurrences in your library": "আপনার লাইব্রেরির সমস্ত ঘটনা",
"Selected text:": "নির্বাচিত টেক্সট:",
"Replace with:": "এর সাথে প্রতিস্থাপন করুন:",
"Enter text...": "টেক্সট লিখুন...",
"Case sensitive:": "কেস সংবেদনশীল:",
"Scope:": "পরিসর:",
"Selection": "নির্বাচন",
"Library": "লাইব্রেরি",
"Yes": "হ্যাঁ",
"No": "না",
"Proofread Replacement Rules": "প্রুফরিড বিকল্প নিয়ম",
"Selected Text Rules": "নির্বাচিত টেক্সট নিয়ম",
"No selected text replacement rules": "কোনও নির্বাচিত টেক্সট বিকল্প নিয়ম নেই",
"Book Specific Rules": "বই নির্দিষ্ট নিয়ম",
"No book-level replacement rules": "কোনও বই-স্তরের বিকল্প নিয়ম নেই",
"Disable Quick Action": "দ্রুত ক্রিয়া নিষ্ক্রিয় করুন",
"Enable Quick Action on Selection": "নির্বাচনের উপর দ্রুত ক্রিয়া সক্ষম করুন",
"None": "কোনোটিই নয়",
"Annotation Tools": "অ্যনোটেশন সরঞ্জামসমূহ",
"Enable Quick Actions": "দ্রুত ক্রিয়া সক্ষম করুন",
"Quick Action": "দ্রুত ক্রিয়া",
"Copy to Notebook": "নোটবুকে কপি করুন",
"Copy text after selection": "নির্বাচনের পরে টেক্সট কপি করুন",
"Highlight text after selection": "নির্বাচনের পরে টেক্সট হাইলাইট করুন",
"Annotate text after selection": "নির্বাচনের পরে টেক্সট মন্তব্য করুন",
"Search text after selection": "নির্বাচনের পরে টেক্সট অনুসন্ধান করুন",
"Look up text in dictionary after selection": "নির্বাচনের পরে টেক্সট অভিধানে দেখুন",
"Translate text after selection": "নির্বাচনের পরে টেক্সট অনুবাদ করুন",
"Read text aloud after selection": "নির্বাচনের পরে টেক্সট উচ্চারণ করুন",
"Proofread text after selection": "নির্বাচনের পরে টেক্সট প্রুফরিড করুন",
"{{activeCount}} active, {{pendingCount}} pending": "{{activeCount}} সক্রিয়, {{pendingCount}} অপেক্ষমাণ",
"{{failedCount}} failed": "{{failedCount}} ব্যর্থ",
"Waiting...": "অপেক্ষা করছে...",
"Failed": "ব্যর্থ",
"Completed": "সম্পন্ন",
"Cancelled": "বাতিল",
"Retry": "পুনরায় চেষ্টা করুন",
"Active": "সক্রিয়",
"Transfer Queue": "স্থানান্তর সারি",
"Upload All": "সব আপলোড করুন",
"Download All": "সব ডাউনলোড করুন",
"Resume Transfers": "স্থানান্তর পুনরায় শুরু করুন",
"Pause Transfers": "স্থানান্তর বিরতি দিন",
"Pending": "অপেক্ষমাণ",
"No transfers": "কোনো স্থানান্তর নেই",
"Retry All": "সব পুনরায় চেষ্টা করুন",
"Clear Completed": "সম্পন্ন মুছুন",
"Clear Failed": "ব্যর্থ মুছুন",
"Upload queued: {{title}}": "আপলোড সারিতে: {{title}}",
"Download queued: {{title}}": "ডাউনলোড সারিতে: {{title}}",
"Book not found in library": "লাইব্রেরিতে বই পাওয়া যায়নি",
"Unknown error": "অজানা ত্রুটি",
"Please log in to continue": "চালিয়ে যেতে লগইন করুন",
"Cloud File Transfers": "ক্লাউড ফাইল স্থানান্তরসমূহ",
"Show Search Results": "অনুসন্ধান ফলাফল দেখান",
"Search results for '{{term}}'": "'{{term}}' এর ফলাফল",
"Close Search": "অনুসন্ধান বন্ধ করুন",
"Previous Result": "আগের ফলাফল",
"Next Result": "পরবর্তী ফলাফল",
"Bookmarks": "বুকমার্ক",
"Annotations": "টীকা",
"Show Results": "ফলাফল দেখুন",
"Clear search": "অনুসন্ধান সাফ করুন",
"Clear search history": "অনুসন্ধান ইতিহাস সাফ করুন",
"Tap to Toggle Footer": "ফুটার টগল করতে আলতো চাপুন",
"Show Current Time": "বর্তমান সময় দেখান",
"Use 24 Hour Clock": "২৪ ঘণ্টার ঘড়ি ব্যবহার করুন",
"Show Current Battery Status": "বর্তমান ব্যাটারি অবস্থা দেখান",
"Exported successfully": "সফলভাবে রপ্তানি হয়েছে",
"Book exported successfully.": "বইটি সফলভাবে রপ্তানি হয়েছে।",
"Failed to export the book.": "বই রপ্তানি করতে ব্যর্থ।",
"Export Book": "বই রপ্তানি করুন",
"Whole word:": "সম্পূর্ণ শব্দ:",
"Date Published": "প্রকাশনার তারিখ",
"Only for TTS:": "শুধুমাত্র TTS-এর জন্য:",
"Uploaded": "আপলোড হয়েছে",
"Downloaded": "ডাউনলোড হয়েছে",
"Deleted": "মুছে ফেলা হয়েছে",
"Note:": "নোট:",
"Time:": "সময়:",
"Format Options": "বিন্যাস বিকল্প",
"Export Date": "রপ্তানি তারিখ",
"Chapter Titles": "অধ্যায়ের শিরোনাম",
"Chapter Separator": "অধ্যায় বিভাজক",
"Highlights": "হাইলাইট",
"Note Date": "নোট তারিখ",
"Advanced": "উন্নত",
"Hide": "লুকান",
"Show": "দেখান",
"Use Custom Template": "কাস্টম টেমপ্লেট ব্যবহার করুন",
"Export Template": "রপ্তানি টেমপ্লেট",
"Template Syntax:": "টেমপ্লেট সিনট্যাক্স:",
"Insert value": "মান সন্নিবেশ করুন",
"Format date (locale)": "তারিখ বিন্যাস (স্থানীয়)",
"Format date (custom)": "তারিখ বিন্যাস (কাস্টম)",
"Conditional": "শর্তসাপেক্ষ",
"Loop": "লুপ",
"Available Variables:": "উপলব্ধ ভেরিয়েবল:",
"Book title": "বইয়ের শিরোনাম",
"Book author": "বইয়ের লেখক",
"Export date": "রপ্তানি তারিখ",
"Array of chapters": "অধ্যায়ের তালিকা",
"Chapter title": "অধ্যায়ের শিরোনাম",
"Array of annotations": "টীকাগুলির তালিকা",
"Highlighted text": "হাইলাইট করা পাঠ্য",
"Annotation note": "টীকা নোট",
"Date Format Tokens:": "তারিখ বিন্যাস টোকেন:",
"Year (4 digits)": "বছর (4 সংখ্যা)",
"Month (01-12)": "মাস (01-12)",
"Day (01-31)": "দিন (01-31)",
"Hour (00-23)": "ঘণ্টা (00-23)",
"Minute (00-59)": "মিনিট (00-59)",
"Second (00-59)": "সেকেন্ড (00-59)",
"Show Source": "উৎস দেখান",
"No content to preview": "প্রিভিউ করার জন্য কোনো কন্টেন্ট নেই",
"Export": "রপ্তানি",
"Set Timeout": "টাইমআউট সেট করুন",
"Select Voice": "ভয়েস নির্বাচন করুন",
"Toggle Sticky Bottom TTS Bar": "স্থির TTS বার টগল করুন",
"Display what I'm reading on Discord": "Discord-এ পড়ছি যা দেখান",
"Show on Discord": "Discord-এ দেখান",
"Instant {{action}}": "তাৎক্ষণিক {{action}}",
"Instant {{action}} Disabled": "তাৎক্ষণিক {{action}} নিষ্ক্রিয়",
"Annotation": "টীকা",
"Reset Template": "টেমপ্লেট রিসেট করুন",
"Annotation style": "টীকা শৈলী",
"Annotation color": "টীকা রঙ",
"Annotation time": "টীকা সময়",
"AI": "কৃত্রিম বুদ্ধিমত্তা (AI)",
"Are you sure you want to re-index this book?": "আপনি কি নিশ্চিত যে আপনি এই বইটি পুনরায় ইনডেক্স করতে চান?",
"Enable AI in Settings": "সেটিংস থেকে AI সক্রিয় করুন",
"Index This Book": "এই বইটি ইনডেক্স করুন",
"Enable AI search and chat for this book": "এই বইটির জন্য AI অনুসন্ধান এবং চ্যাট সক্রিয় করুন",
"Start Indexing": "ইনডেক্সিং শুরু করুন",
"Indexing book...": "বই ইনডেক্স করা হচ্ছে...",
"Preparing...": "প্রস্তুত করা হচ্ছে...",
"Delete this conversation?": "এই কথোপকথনটি মুছবেন?",
"No conversations yet": "এখনও কোনও কথোপকথন নেই",
"Start a new chat to ask questions about this book": "এই বইটি সম্পর্কে প্রশ্ন জিজ্ঞাসা করতে একটি নতুন চ্যাট শুরু করুন",
"Rename": "নাম পরিবর্তন",
"New Chat": "নতুন চ্যাট",
"Chat": "চ্যাট",
"Please enter a model ID": "দয়া করে একটি মডেল আইডি লিখুন",
"Model not available or invalid": "মডেল উপলব্ধ নয় বা অবৈধ",
"Failed to validate model": "মডেল যাচাই করতে ব্যর্থ হয়েছে",
"Couldn't connect to Ollama. Is it running?": "Ollama এর সাথে সংযোগ করা যায়নি। এটি কি চলছে?",
"Invalid API key or connection failed": "অবৈধ API কী বা সংযোগ ব্যর্থ হয়েছে",
"Connection failed": "সংযোগ ব্যর্থ হয়েছে",
"AI Assistant": "AI সহকারী",
"Enable AI Assistant": "AI সহকারী সক্রিয় করুন",
"Provider": "প্রদানকারী",
"Ollama (Local)": "Ollama (স্থানীয়)",
"AI Gateway (Cloud)": "AI গেটওয়ে (ক্লাউড)",
"Ollama Configuration": "Ollama কনফিগারেশন",
"Refresh Models": "মডেল রিফ্রেশ করুন",
"AI Model": "AI মডেল",
"No models detected": "কোনও মডেল শনাক্ত করা যায়নি",
"AI Gateway Configuration": "AI গেটওয়ে কনফিগারেশন",
"Choose from a selection of high-quality, economical AI models. You can also bring your own model by selecting \"Custom Model\" below.": "উচ্চ-মানের, সাশ্রয়ী AI মডেলগুলি থেকে নির্বাচন করুন। আপনি নিচে \"কাস্টম মডেল\" নির্বাচন করে আপনার নিজস্ব মডেলও আনতে পারেন।",
"API Key": "API কী",
"Get Key": "কী পান",
"Model": "মডেল",
"Custom Model...": "কাস্টম মডেল...",
"Custom Model ID": "কাস্টম মডেল আইডি",
"Validate": "যাচাই করুন",
"Model available": "মডেল উপলব্ধ",
"Connection": "সংযোগ",
"Test Connection": "সংযোগ পরীক্ষা করুন",
"Connected": "সংযুক্ত",
"Custom Colors": "কাস্টম রঙ",
"Color E-Ink Mode": "কালার ই-ইঙ্ক মোড",
"Reading Ruler": "রিডিং রুলার",
"Enable Reading Ruler": "রিডিং রুলার সক্ষম করুন",
"Lines to Highlight": "হাইলাইট করার জন্য লাইন",
"Ruler Color": "রুলারের রঙ",
"Command Palette": "কমান্ড প্যালেট",
"Search settings and actions...": "সেটিংস এবং অ্যাকশন খুঁজুন...",
"No results found for": "এর জন্য কোনো ফলাফল পাওয়া যায়নি",
"Type to search settings and actions": "সেটিংস এবং অ্যাকশন খুঁজতে টাইপ করুন",
"Recent": "সাম্প্রতিক",
"navigate": "নেভিগেট",
"select": "নির্বাচন করুন",
"close": "বন্ধ করুন",
"Search Settings": "সেটিংস খুঁজুন",
"Page Margins": "পৃষ্ঠার মার্জিন",
"AI Provider": "AI প্রদানকারী",
"Ollama URL": "Ollama URL",
"Ollama Model": "Ollama মডেল",
"AI Gateway Model": "AI গেটওয়ে মডেল",
"Actions": "অ্যাকশন",
"Navigation": "নেভিগেশন",
"Set status for {{count}} book(s)_one": "{{count}} টি বইয়ের স্ট্যাটাস সেট করুন",
"Set status for {{count}} book(s)_other": "{{count}} টি বইয়ের স্ট্যাটাস সেট করুন",
"Mark as Unread": "পড়া হয়নি হিসেবে চিহ্নিত করুন",
"Mark as Finished": "পড়া শেষ হিসেবে চিহ্নিত করুন",
"Finished": "শেষ হয়েছে",
"Unread": "অপঠিত",
"Clear Status": "স্থিতি মুছুন",
"Status": "অবস্থা",
"Loading": "লোড হচ্ছে...",
"Exit Paragraph Mode": "অনুচ্ছেদ মোড থেকে প্রস্থান করুন",
"Paragraph Mode": "অনুচ্ছেদ মোড",
"Embedding Model": "এম্বেডিং মডেল",
"{{count}} book(s) synced_one": "{{count}}টি বই সিঙ্ক করা হয়েছে",
"{{count}} book(s) synced_other": "{{count}}টি বই সিঙ্ক করা হয়েছে",
"Unable to start RSVP": "RSVP शुरू করতে অসমর্থ",
"RSVP not supported for PDF": "PDF-এর জন্য RSVP সমর্থিত নয়",
"Select Chapter": "অধ্যায় নির্বাচন করুন",
"Context": "প্রসঙ্গ",
"Ready": "প্রস্তুত",
"Chapter Progress": "অধ্যায় অগ্রগতি",
"words": "শব্দ",
"{{time}} left": "{{time}} বাকি",
"Reading progress": "পড়ার অগ্রগতি",
"Skip back 15 words": "১৫ শব্দ পিছিয়ে যান",
"Back 15 words (Shift+Left)": "১৫ শব্দ পিছিয়ে যান (Shift+Left)",
"Pause (Space)": "বিরতি (Space)",
"Play (Space)": "চালান (Space)",
"Skip forward 15 words": "১৫ শব্দ এগিয়ে যান",
"Forward 15 words (Shift+Right)": "১৫ শব্দ এগিয়ে যান (Shift+Right)",
"Decrease speed": "গতি কমান",
"Slower (Left/Down)": "ধীর (Left/Down)",
"Increase speed": "গতি বাড়ান",
"Faster (Right/Up)": "দ্রুত (Right/Up)",
"Start RSVP Reading": "RSVP পড়া শুরু করুন",
"Choose where to start reading": "কোথা থেকে পড়া শুরু করবেন তা চয়ন করুন",
"From Chapter Start": "অধ্যায় শুরু থেকে",
"Start reading from the beginning of the chapter": "অধ্যায়ের শুরু থেকে পড়া শুরু করুন",
"Resume": "পুনরায় শুরু করুন",
"Continue from where you left off": "যেখানে আপনি ছেড়েছিলেন সেখান থেকে চালিয়ে যান",
"From Current Page": "বর্তমান পৃষ্ঠা থেকে",
"Start from where you are currently reading": "আপনি বর্তমানে যেখানে পড়ছেন সেখান থেকে শুরু করুন",
"From Selection": "নির্বাচন থেকে",
"Speed Reading Mode": "দ্রুত পাঠ্য মোড",
"Scroll left": "বামে স্ক্রোল করুন",
"Scroll right": "ডানে স্ক্রোল করুন",
"Library Sync Progress": "লাইব্রেরি সিঙ্ক অগ্রগতি",
"Back to library": "লাইব্রেরিতে ফিরে যান",
"Group by...": "গ্রুপ অনুযায়ী...",
"Export as Plain Text": "সাধারণ টেক্সট হিসেবে এক্সপোর্ট করুন",
"Export as Markdown": "Markdown হিসেবে এক্সপোর্ট করুন",
"Show Page Navigation Buttons": "নেভিগেশন বোতাম",
"Page {{number}}": "পৃষ্ঠা {{number}}",
"highlight": "হাইলাইট",
"underline": "আন্ডারলাইন",
"squiggly": "এঁকেবেঁকে চলা",
"red": "লাল",
"violet": "বেগুনী",
"blue": "নীল",
"green": "সবুজ",
"yellow": "হলুদ",
"Select {{style}} style": "{{style}} স্টাইল নির্বাচন করুন",
"Select {{color}} color": "{{color}} রঙ নির্বাচন করুন",
"Close Book": "বই বন্ধ করুন",
"Speed Reading": "দ্রুত পঠন",
"Close Speed Reading": "দ্রুত পঠন বন্ধ করুন",
"Authors": "লেখকগণ",
"Books": "বই",
"Groups": "দল",
"Back to TTS Location": "টিটিএস অবস্থানে ফিরে যান",
"Metadata": "মেটাডেটা",
"Image viewer": "চিত্র প্রদর্শক",
"Previous Image": "পূর্ববর্তী চিত্র",
"Next Image": "পরবর্তী চিত্র",
"Zoomed": "জুম করা হয়েছে",
"Zoom level": "জুম স্তর",
"Table viewer": "টেবিল প্রদর্শক",
"Unable to connect to Readwise. Please check your network connection.": "Readwise এর সাথে সংযোগ করতে অক্ষম। আপনার নেটওয়ার্ক সংযোগটি পরীক্ষা করুন।",
"Invalid Readwise access token": "অকার্যকর Readwise অ্যাক্সেস টোকেন",
"Disconnected from Readwise": "Readwise থেকে সংযোগ বিচ্ছিন্ন হয়েছে",
"Never": "কখনো না",
"Sync Enabled": "সিঙ্কিং সক্ষম",
"Disconnect": "সংযোগ বিচ্ছিন্ন করুন",
"Connect your Readwise account to sync highlights.": "হাইলাইটগুলি সিঙ্ক করার জন্য আপনার Readwise অ্যাকাউন্টটি সংযোগ করুন।",
"Get your access token at": "আপনার অ্যাক্সেস টোকেনটি এখানে পাবেন",
"Access Token": "অ্যাক্সেস টোকেন",
"Paste your Readwise access token": "আপনার Readwise অ্যাক্সেস টোকেনটি পেস্ট করুন",
"Readwise Sync": "Readwise সিঙ্ক",
"Push Highlights": "হাইলাইট পাঠান",
"Highlights synced to Readwise": "হাইলাইটগুলি Readwise এ সিঙ্ক করা হয়েছে",
"Readwise sync failed: no internet connection": "Readwise সিঙ্ক ব্যর্থ হয়েছে: ইন্টারনেট সংযোগ নেই",
"Readwise sync failed: {{error}}": "Readwise সিঙ্ক ব্যর্থ হয়েছে: {{error}}",
"System Screen Brightness": "সিস্টেম স্ক্রিন উজ্জ্বলতা",
"Page:": "পৃষ্ঠা:",
"Page: {{number}}": "পৃষ্ঠা: {{number}}",
"Annotation page number": "টীকা পৃষ্ঠা নম্বর",
"Translating...": "অনুবাদ করা হচ্ছে...",
"Show Battery Percentage": "ব্যাটারি শতাংশ দেখান",
"Hide Scrollbar": "স্ক্রলবার লুকান",
"Skip to last reading position": "শেষ পড়ার অবস্থানে যান",
"Show context": "প্রসঙ্গ দেখান",
"Hide context": "প্রসঙ্গ লুকান",
"Decrease font size": "ফন্টের আকার কমান",
"Increase font size": "ফন্টের আকার বাড়ান",
"Focus": "ফোকাস",
"Theme color": "থিম রঙ",
"Import failed": "আমদানি ব্যর্থ",
"Available Formatters:": "উপলব্ধ ফরম্যাটার:",
"Format date": "তারিখ ফরম্যাট",
"Markdown block quote (> per line)": "Markdown ব্লক কোট (প্রতি লাইনে >)",
"Newlines to <br>": "নতুন লাইন থেকে <br>",
"Change case": "অক্ষরের কেস পরিবর্তন",
"Trim whitespace": "শূন্যস্থান ছাঁটা",
"Truncate to n characters": "n অক্ষরে সংক্ষিপ্ত করুন",
"Replace text": "টেক্সট প্রতিস্থাপন",
"Fallback value": "ফলব্যাক মান",
"Get length": "দৈর্ঘ্য নিন",
"First/last element": "প্রথম/শেষ উপাদান",
"Join array": "অ্যারে যুক্ত করুন",
"Backup failed: {{error}}": "ব্যাকআপ ব্যর্থ: {{error}}",
"Select Backup": "ব্যাকআপ নির্বাচন করুন",
"Restore failed: {{error}}": "পুনরুদ্ধার ব্যর্থ: {{error}}",
"Backup & Restore": "ব্যাকআপ ও পুনরুদ্ধার",
"Create a backup of your library or restore from a previous backup. Restoring will merge with your current library.": "আপনার লাইব্রেরির ব্যাকআপ তৈরি করুন বা পূর্ববর্তী ব্যাকআপ থেকে পুনরুদ্ধার করুন। পুনরুদ্ধার আপনার বর্তমান লাইব্রেরির সাথে মার্জ হবে।",
"Backup Library": "লাইব্রেরি ব্যাকআপ",
"Restore Library": "লাইব্রেরি পুনরুদ্ধার",
"Creating backup...": "ব্যাকআপ তৈরি হচ্ছে...",
"Restoring library...": "লাইব্রেরি পুনরুদ্ধার হচ্ছে...",
"{{current}} of {{total}} items": "{{current}} / {{total}} আইটেম",
"Backup completed successfully!": "ব্যাকআপ সফলভাবে সম্পন্ন!",
"Restore completed successfully!": "পুনরুদ্ধার সফলভাবে সম্পন্ন!",
"Your library has been saved to the selected location.": "আপনার লাইব্রেরি নির্বাচিত স্থানে সংরক্ষিত হয়েছে।",
"{{added}} books added, {{updated}} books updated.": "{{added}}টি বই যোগ হয়েছে, {{updated}}টি বই আপডেট হয়েছে।",
"Operation failed": "অপারেশন ব্যর্থ",
"Loading library...": "লাইব্রেরি লোড হচ্ছে...",
"{{count}} books refreshed_one": "{{count}}টি বইয়ের মেটাডেটা রিফ্রেশ হয়েছে",
"{{count}} books refreshed_other": "{{count}}টি বইয়ের মেটাডেটা রিফ্রেশ হয়েছে",
"Failed to refresh metadata": "মেটাডেটা রিফ্রেশ ব্যর্থ",
"Refresh Metadata": "মেটাডেটা রিফ্রেশ",
"Keyboard Shortcuts": "কীবোর্ড শর্টকাট",
"View all keyboard shortcuts": "সমস্ত কীবোর্ড শর্টকাট দেখুন",
"Switch Sidebar Tab": "সাইডবার ট্যাব পরিবর্তন করুন",
"Toggle Notebook": "নোটবুক দেখান/লুকান",
"Search in Book": "বইয়ে অনুসন্ধান করুন",
"Toggle Scroll Mode": "স্ক্রল মোড টগল করুন",
"Toggle Select Mode": "নির্বাচন মোড টগল করুন",
"Toggle Bookmark": "বুকমার্ক টগল করুন",
"Toggle Text to Speech": "টেক্সট টু স্পিচ টগল করুন",
"Play / Pause TTS": "TTS চালান / বিরতি দিন",
"Toggle Paragraph Mode": "অনুচ্ছেদ মোড টগল করুন",
"Highlight Selection": "নির্বাচিত অংশ হাইলাইট করুন",
"Underline Selection": "নির্বাচিত অংশে আন্ডারলাইন করুন",
"Annotate Selection": "নির্বাচিত অংশে টীকা যোগ করুন",
"Search Selection": "নির্বাচিত অংশ অনুসন্ধান করুন",
"Copy Selection": "নির্বাচিত অংশ কপি করুন",
"Translate Selection": "নির্বাচিত অংশ অনুবাদ করুন",
"Dictionary Lookup": "অভিধানে খুঁজুন",
"Read Aloud Selection": "নির্বাচিত অংশ জোরে পড়ুন",
"Proofread Selection": "নির্বাচিত অংশ প্রুফরিড করুন",
"Open Settings": "সেটিংস খুলুন",
"Open Command Palette": "কমান্ড প্যালেট খুলুন",
"Show Keyboard Shortcuts": "কীবোর্ড শর্টকাট দেখান",
"Open Books": "বই খুলুন",
"Toggle Fullscreen": "পূর্ণ পর্দা টগল করুন",
"Close Window": "উইন্ডো বন্ধ করুন",
"Quit App": "অ্যাপ থেকে প্রস্থান করুন",
"Go Left / Previous Page": "বামে যান / আগের পৃষ্ঠা",
"Go Right / Next Page": "ডানে যান / পরের পৃষ্ঠা",
"Go Up": "উপরে যান",
"Go Down": "নিচে যান",
"Previous Chapter": "আগের অধ্যায়",
"Next Chapter": "পরের অধ্যায়",
"Scroll Half Page Down": "অর্ধেক পৃষ্ঠা নিচে স্ক্রল করুন",
"Scroll Half Page Up": "অর্ধেক পৃষ্ঠা উপরে স্ক্রল করুন",
"Save Note": "নোট সংরক্ষণ করুন",
"Single Section Scroll": "একক অধ্যায় স্ক্রল",
"General": "সাধারণ",
"Text to Speech": "পাঠ থেকে বাক্য",
"Zoom": "জুম",
"Window": "উইন্ডো",
"Your Bookshelf": "আপনার বইয়ের তাক",
"TTS": "টেক্সট টু স্পিচ",
"Media Info": "মিডিয়া তথ্য",
"Update Frequency": "আপডেটের ফ্রিকোয়েন্সি",
"Every Sentence": "প্রতিটি বাক্য",
"Every Paragraph": "প্রতিটি অনুচ্ছেদ",
"Every Chapter": "প্রতিটি অধ্যায়",
"TTS Media Info Update Frequency": "TTS মিডিয়া তথ্য আপডেট ফ্রিকোয়েন্সি",
"Select reading speed": "পড়ার গতি নির্বাচন করুন",
"Drag to seek": "খুঁজতে টানুন",
"Punctuation Delay": "বিরাম চিহ্ন বিলম্ব",
"Please confirm that this OPDS connection will be proxied through Readest servers on the web app before continuing.": "অনুগ্রহ করে নিশ্চিত করুন যে এই OPDS সংযোগটি ওয়েব অ্যাপে Readest সার্ভারের মাধ্যমে প্রক্সি করা হবে।",
"Custom Headers (optional)": "কাস্টম হেডার (ঐচ্ছিক)",
"Add one header per line using \"Header-Name: value\".": "প্রতি লাইনে একটি হেডার যোগ করুন \"Header-Name: value\" ব্যবহার করে।",
"I understand this OPDS connection will be proxied through Readest servers on the web app. If I do not trust Readest with these credentials or headers, I should use the native app instead.": "আমি বুঝতে পারছি যে এই OPDS সংযোগটি ওয়েব অ্যাপে Readest সার্ভারের মাধ্যমে প্রক্সি করা হবে। আমি যদি এই শংসাপত্র বা হেডারগুলির সাথে Readest-কে বিশ্বাস না করি, তবে আমার পরিবর্তে নেটিভ অ্যাপটি ব্যবহার করা উচিত।",
"Unable to connect to Hardcover. Please check your network connection.": "Hardcover-এ সংযোগ করা যাচ্ছে না। অনুগ্রহ করে আপনার নেটওয়ার্ক সংযোগ পরীক্ষা করুন।",
"Invalid Hardcover API token": "অবৈধ Hardcover API টোকেন",
"Disconnected from Hardcover": "Hardcover থেকে সংযোগ বিচ্ছিন্ন",
"Connect your Hardcover account to sync reading progress and notes.": "পড়ার অগ্রগতি এবং নোট সিঙ্ক করতে আপনার Hardcover অ্যাকাউন্ট সংযুক্ত করুন।",
"Get your API token from hardcover.app → Settings → API.": "hardcover.app → সেটিংস → API থেকে আপনার API টোকেন পান।",
"API Token": "API টোকেন",
"Paste your Hardcover API token": "আপনার Hardcover API টোকেন পেস্ট করুন",
"Hardcover Sync": "Hardcover সিঙ্ক",
"Push Notes": "নোট পুশ করুন",
"No annotations or excerpts to sync for this book.": "এই বইয়ের জন্য সিঙ্ক করার মতো কোনো টীকা বা উদ্ধৃতি নেই।",
"Configure Hardcover in Settings first.": "প্রথমে সেটিংসে Hardcover কনফিগার করুন।",
"No new Hardcover note changes to sync.": "সিঙ্ক করার জন্য নতুন কোনো Hardcover নোট পরিবর্তন নেই।",
"Hardcover synced: {{inserted}} new, {{updated}} updated, {{skipped}} unchanged": "Hardcover সিঙ্ক সম্পন্ন: {{inserted}} নতুন, {{updated}} আপডেট, {{skipped}} অপরিবর্তিত",
"Hardcover notes sync failed: {{error}}": "Hardcover নোট সিঙ্ক ব্যর্থ: {{error}}",
"Reading progress synced to Hardcover": "পড়ার অগ্রগতি Hardcover-এ সিঙ্ক হয়েছে",
"Hardcover progress sync failed: {{error}}": "Hardcover অগ্রগতি সিঙ্ক ব্যর্থ: {{error}}",
"ISBN": "ISBN",
"Keep existing source identifier": "বিদ্যমান উৎস শনাক্তকারী রাখুন",
"Toggle Toolbar": "টুলবার টগল করুন",
"Split Hyphens": "হাইফেন বিভক্ত করুন",
"Apply Theme Colors to PDF": "PDF-এ থিম রং প্রয়োগ করুন",
"Name": "নাম",
"Remove": "সরান",
"Edit OPDS Catalog": "OPDS ক্যাটালগ সম্পাদনা",
"Save Changes": "পরিবর্তন সংরক্ষণ",
"Use Book Layout": "বইয়ের লেআউট ব্যবহার করুন",
"End of this section. Continue to the next.": "এই বিভাগের শেষ। পরবর্তী বিভাগে চালিয়ে যান।",
"Auto-download": "স্বয়ংক্রিয় ডাউনলোড",
"Auto-download new items": "নতুন আইটেম স্বয়ংক্রিয়ভাবে ডাউনলোড করুন",
"Automatically download new publications when the app syncs": "অ্যাপ সিঙ্ক হওয়ার সময় নতুন প্রকাশনা স্বয়ংক্রিয়ভাবে ডাউনলোড করুন",
"{{count}} new item(s) downloaded from OPDS_one": "OPDS থেকে {{count}}টি নতুন আইটেম ডাউনলোড হয়েছে",
"{{count}} new item(s) downloaded from OPDS_other": "OPDS থেকে {{count}}টি নতুন আইটেম ডাউনলোড হয়েছে",
"Failed to sync {{count}} OPDS catalog(s)_one": "{{count}}টি OPDS ক্যাটালগ সিঙ্ক করতে ব্যর্থ",
"Failed to sync {{count}} OPDS catalog(s)_other": "{{count}}টি OPDS ক্যাটালগ সিঙ্ক করতে ব্যর্থ",
"Last synced {{when}}": "শেষ সিঙ্ক হয়েছে {{when}}",
"Failed downloads": "ব্যর্থ ডাউনলোড",
"No failed downloads": "কোনো ব্যর্থ ডাউনলোড নেই",
"Attempts: {{count}}_one": "প্রচেষ্টা: {{count}}",
"Attempts: {{count}}_other": "প্রচেষ্টা: {{count}}",
"Skip": "এড়িয়ে যান",
"Skip all": "সব এড়িয়ে যান",
"Retry all": "সব পুনরায় চেষ্টা করুন",
"{{count}} failed_one": "{{count}}টি ব্যর্থ",
"{{count}} failed_other": "{{count}}টি ব্যর্থ",
"(none)": "(কোনোটি না)",
"Identifiers": "শনাক্তকারী",
"Read (Stream)": "পড়ুন (স্ট্রিম)",
"Failed to start stream": "স্ট্রিম শুরু করতে ব্যর্থ",
"No dictionaries enabled": "কোনো অভিধান সক্রিয় নেই",
"Enable a dictionary in Settings → Language → Dictionaries.": "সেটিংস → ভাষা → অভিধান থেকে একটি অভিধান সক্রিয় করুন।",
"Wiktionary": "উইক্‌শনারি",
"Drag to reorder": "পুনঃক্রম করতে টানুন",
"Built-in": "বিল্ট-ইন",
"Bundle is missing on this device. Re-import to use it.": "এই ডিভাইসে বান্ডলটি নেই। ব্যবহার করতে পুনরায় আমদানি করুন।",
"This dictionary format is not supported.": "এই অভিধান বিন্যাস সমর্থিত নয়।",
"MDict": "MDict",
"StarDict": "StarDict",
"Imported {{count}} dictionary_one": "{{count}}টি অভিধান আমদানি করা হয়েছে",
"Imported {{count}} dictionary_other": "{{count}}টি অভিধান আমদানি করা হয়েছে",
"Skipped incomplete bundles: {{names}}": "অসম্পূর্ণ বান্ডল এড়ানো হয়েছে: {{names}}",
"Failed to import dictionary: {{message}}": "অভিধান আমদানি ব্যর্থ: {{message}}",
"Dictionaries": "অভিধান",
"Delete Dictionary": "অভিধান মুছুন",
"Importing…": "আমদানি হচ্ছে…",
"Import Dictionary": "অভিধান আমদানি করুন",
"No dictionaries available.": "কোনো অভিধান উপলব্ধ নেই।",
"StarDict bundles need .ifo, .idx, and .dict.dz files (.syn optional).": "StarDict বান্ডলে .ifo, .idx এবং .dict.dz ফাইল প্রয়োজন (.syn ঐচ্ছিক)।",
"Select all the bundle files together when importing.": "আমদানির সময় বান্ডলের সব ফাইল একসঙ্গে নির্বাচন করুন।",
"Manage Dictionaries": "অভিধান পরিচালনা",
"Select Dictionary Files": "অভিধান ফাইল নির্বাচন করুন",
"Read on Wikipedia →": "উইকিপিডিয়ায় পড়ুন →",
"This link can't be opened": "এই লিঙ্কটি খোলা যাচ্ছে না",
"The annotation link is missing required information. The original link may have been truncated.": "অ্যানোটেশন লিঙ্কে প্রয়োজনীয় তথ্য নেই। মূল লিঙ্কটি কাটা পড়ে থাকতে পারে।",
"Go to Readest": "Readest-এ যান",
"Open-source ebook reader for everyone, on every device.": "সবার জন্য, প্রতিটি ডিভাইসের জন্য — ওপেন-সোর্স ইবুক রিডার।",
"Open in Readest": "Readest-এ খুলুন",
"If Readest didn't open automatically, choose an option below:": "Readest স্বয়ংক্রিয়ভাবে না খুললে, নিচের একটি বিকল্প বেছে নিন:",
"Continue reading where you left off.": "যেখানে রেখেছিলেন, সেখান থেকেই পড়া চালিয়ে যান।",
"Readest logo": "Readest লোগো",
"Opening Readest...": "Readest খোলা হচ্ছে…",
"Open in Readest app": "Readest অ্যাপে খুলুন",
"Continue in browser": "ব্রাউজারে চালিয়ে যান",
"Don't have Readest?": "Readest নেই?",
"Book not in your library": "এই বইটি আপনার লাইব্রেরিতে নেই",
"Share Book": "বই শেয়ার করুন",
"Could not create share link": "শেয়ার লিঙ্ক তৈরি করা যায়নি",
"Link copied": "লিঙ্ক কপি হয়েছে",
"Could not copy link": "লিঙ্ক কপি করা যায়নি",
"Share revoked": "শেয়ার বাতিল হয়েছে",
"Could not revoke share": "শেয়ার বাতিল করা যায়নি",
"Uploading book…": "বই আপলোড হচ্ছে…",
"Generating…": "তৈরি হচ্ছে…",
"Generate share link": "শেয়ার লিঙ্ক তৈরি করুন",
"Share URL": "শেয়ার URL",
"Copy link": "লিঙ্ক কপি করুন",
"Copied": "কপি হয়েছে",
"Share via…": "মাধ্যমে শেয়ার করুন…",
"Expires {{date}}": "{{date}} এ মেয়াদ শেষ",
"Revoking…": "বাতিল হচ্ছে…",
"Revoke share": "শেয়ার বাতিল করুন",
"Sign in to share books": "বই শেয়ার করতে সাইন ইন করুন",
"Expiring soon": "শীঘ্রই মেয়াদ শেষ হবে",
"Missing share token": "শেয়ার টোকেন অনুপস্থিত",
"Could not add to your library": "আপনার লাইব্রেরিতে যোগ করা যায়নি",
"This share link is no longer available": "এই শেয়ার লিঙ্কটি আর উপলব্ধ নেই",
"The original link may have expired or been revoked.": "মূল লিঙ্কটির মেয়াদ শেষ হয়ে থাকতে পারে বা বাতিল হয়েছে।",
"Get Readest": "Readest পান",
"Loading shared book…": "শেয়ার করা বই লোড হচ্ছে…",
"Adding…": "যোগ করা হচ্ছে…",
"Add to my library": "আমার লাইব্রেরিতে যোগ করুন",
"Could not load your shares": "আপনার শেয়ারগুলো লোড করা যায়নি",
"Revoked": "বাতিল",
"Expired": "মেয়াদোত্তীর্ণ",
"Shared books": "শেয়ার করা বই",
"You haven't shared any books yet": "আপনি এখনও কোনো বই শেয়ার করেননি",
"Open a book and tap Share to send it to a friend.": "একটি বই খুলুন এবং বন্ধুকে পাঠাতে শেয়ার ট্যাপ করুন।",
"{{count}} active_one": "{{count}}টি সক্রিয়",
"{{count}} active_other": "{{count}}টি সক্রিয়",
"{{count}} downloads_one": "{{count}}টি ডাউনলোড",
"{{count}} downloads_other": "{{count}}টি ডাউনলোড",
"starts at saved page": "সংরক্ষিত পৃষ্ঠা থেকে শুরু হয়",
"More actions": "আরও ক্রিয়াকলাপ",
"Loading…": "লোড হচ্ছে…",
"Load more": "আরও লোড করুন",
"Could not load shared book": "শেয়ার করা বই লোড করা যায়নি",
"The share link is missing required information.": "শেয়ার লিঙ্কে প্রয়োজনীয় তথ্য নেই।",
"Please check your connection and try again.": "অনুগ্রহ করে আপনার সংযোগ পরীক্ষা করুন এবং আবার চেষ্টা করুন।",
"Sign in to import shared books": "শেয়ার করা বই আমদানি করতে সাইন ইন করুন",
"Already in your library": "ইতিমধ্যে আপনার লাইব্রেরিতে রয়েছে",
"Added to your library": "আপনার লাইব্রেরিতে যোগ করা হয়েছে",
"Could not import shared book": "শেয়ার করা বই আমদানি করা যায়নি",
"Manage Shared Links": "শেয়ার লিঙ্ক পরিচালনা করুন",
"Expires in": "মেয়াদ শেষ হবে",
"{{count}} days_one": "১ দিন",
"{{count}} days_other": "{{count}} দিন",
"Expires in {{count}} days_one": "১ দিনে মেয়াদ শেষ",
"Expires in {{count}} days_other": "{{count}} দিনে মেয়াদ শেষ",
"Expires in {{count}} hours_one": "১ ঘণ্টায় মেয়াদ শেষ",
"Expires in {{count}} hours_other": "{{count}} ঘণ্টায় মেয়াদ শেষ",
"Open in app": "অ্যাপে খুলুন",
"Shared with you": "আপনার সাথে শেয়ার করা",
"Downloading… {{percent}}%": "ডাউনলোড হচ্ছে… {{percent}}%",
"Import progress": "আমদানি অগ্রগতি",
"Share reading progress": "পড়ার অগ্রগতি শেয়ার করুন",
"Includes your reading progress": "আপনার পড়ার অগ্রগতি অন্তর্ভুক্ত",
"Synced {{time}}": "{{time}} সিঙ্ক হয়েছে",
"Sync Info": "সিঙ্ক তথ্য",
"Last Synced": "শেষ সিঙ্ক",
"DICT": "DICT",
"Slob": "Slob",
"DICT bundles need a .index file and a .dict.dz file.": "DICT বান্ডেলের জন্য একটি .index ফাইল এবং একটি .dict.dz ফাইল প্রয়োজন।",
"Slob bundles need a .slob file.": "Slob বান্ডেলের জন্য একটি .slob ফাইল প্রয়োজন।",
"URL template must start with http(s):// and contain %WORD%.": "URL টেমপ্লেটটি অবশ্যই http(s):// দিয়ে শুরু হতে হবে এবং %WORD% থাকতে হবে।",
"Web": "ওয়েব",
"Add Web Search": "ওয়েব অনুসন্ধান যোগ করুন",
"Edit Web Search": "ওয়েব অনুসন্ধান সম্পাদনা করুন",
"e.g. Google": "যেমন Google",
"URL Template": "URL টেমপ্লেট",
"Use %WORD% where the looked-up word should appear.": "যেখানে অনুসন্ধান করা শব্দ উপস্থিত হওয়া উচিত সেখানে %WORD% ব্যবহার করুন।",
"Open the search result in your browser:": "আপনার ব্রাউজারে অনুসন্ধান ফলাফল খুলুন:",
"Open in {{name}}": "{{name}}-এ খুলুন",
"Book Fingerprint": "বইয়ের ফিঙ্গারপ্রিন্ট",
"Search the web": "ওয়েবে অনুসন্ধান করুন",
"App deeplink (readest://)": "অ্যাপ ডিপলিংক (readest://)",
"Universal web link (https://)": "সর্বজনীন ওয়েব লিঙ্ক (https://)",
"Name cannot be empty.": "নাম খালি রাখা যাবে না।",
"Replaced {{count}} existing dictionary_one": "{{count}}টি বিদ্যমান অভিধান প্রতিস্থাপিত হয়েছে",
"Replaced {{count}} existing dictionary_other": "{{count}}টি বিদ্যমান অভিধান প্রতিস্থাপিত হয়েছে",
"Cancel Edit": "সম্পাদনা বাতিল করুন",
"Edit Dictionary": "অভিধান সম্পাদনা করুন",
"MDict bundles use .mdx files; companion .mdd and .css files are optional.": "MDict বান্ডেল .mdx ফাইল ব্যবহার করে; সহগামী .mdd ও .css ফাইল ঐচ্ছিক।",
"Dictionary name": "অভিধানের নাম",
"This audio can't play here — the dictionary uses an outdated format. Try one with Opus, MP3, or WAV audio.": "এই অডিও এখানে চালানো যাচ্ছে না — অভিধানটি একটি পুরনো ফর্ম্যাট ব্যবহার করছে। Opus, MP3 বা WAV অডিও সহ একটি ব্যবহার করে দেখুন।",
"File uploaded: {{title}}": "ফাইল আপলোড হয়েছে: {{title}}",
"File downloaded: {{title}}": "ফাইল ডাউনলোড হয়েছে: {{title}}",
"Deleted cloud copy of the file: {{title}}": "ফাইলের ক্লাউড অনুলিপি মুছে ফেলা হয়েছে: {{title}}",
"Failed to upload file: {{title}}": "ফাইল আপলোড ব্যর্থ: {{title}}",
"Failed to download file: {{title}}": "ফাইল ডাউনলোড ব্যর্থ: {{title}}",
"Failed to delete cloud copy of the file: {{title}}": "ফাইলের ক্লাউড অনুলিপি মুছতে ব্যর্থ: {{title}}",
"{{percentage}}% used": "{{percentage}}% ব্যবহৃত",
"Set PIN…": "PIN সেট করুন…",
"Require a 4-digit PIN to open Readest": "Readest খুলতে একটি ৪-সংখ্যার PIN প্রয়োজন করুন",
"Change PIN…": "PIN পরিবর্তন করুন…",
"Disable PIN…": "PIN নিষ্ক্রিয় করুন…",
"Sync passphrase ready": "সিঙ্ক পাসফ্রেজ প্রস্তুত",
"This permanently deletes the encrypted credentials we sync (e.g., OPDS catalog passwords) on every device. Local copies are preserved. You will need to re-enter the sync passphrase or set a new one. Continue?": "এটি প্রতিটি ডিভাইসে আমরা সিঙ্ক করি এমন এনক্রিপ্ট করা শংসাপত্র (যেমন, OPDS ক্যাটালগ পাসওয়ার্ড) স্থায়ীভাবে মুছে ফেলে। স্থানীয় কপিগুলি সংরক্ষিত থাকে। আপনাকে সিঙ্ক পাসফ্রেজ পুনরায় প্রবেশ করতে হবে বা একটি নতুন সেট করতে হবে। চালিয়ে যাবেন?",
"Sync passphrase forgotten — all encrypted fields cleared": "সিঙ্ক পাসফ্রেজ ভুলে গিয়েছেন — সমস্ত এনক্রিপ্ট করা ক্ষেত্র সাফ করা হয়েছে",
"Sync passphrase": "সিঙ্ক পাসফ্রেজ",
"Set passphrase": "পাসফ্রেজ সেট করুন",
"Forgot passphrase": "পাসফ্রেজ ভুলে গেছেন",
"Incorrect PIN": "ভুল PIN",
"App locked": "অ্যাপ লক করা হয়েছে",
"Enter your PIN": "আপনার PIN লিখুন",
"Readest is locked. Enter your 4-digit PIN to continue.": "Readest লক করা আছে। চালিয়ে যেতে আপনার ৪-সংখ্যার PIN লিখুন।",
"PIN code": "PIN কোড",
"Forgetting your PIN locks you out of this device. You'll need to clear the app's data to reset it.": "PIN ভুলে গেলে আপনি এই ডিভাইসে প্রবেশ করতে পারবেন না। এটি রিসেট করতে অ্যাপের ডেটা মুছে ফেলতে হবে।",
"Passphrase must be at least 8 characters": "পাসফ্রেজ কমপক্ষে 8 অক্ষরের হতে হবে",
"Passphrases do not match": "পাসফ্রেজগুলি মিলছে না",
"Set sync passphrase": "সিঙ্ক পাসফ্রেজ সেট করুন",
"Enter sync passphrase": "সিঙ্ক পাসফ্রেজ লিখুন",
"A sync passphrase encrypts your sensitive fields (like OPDS catalog credentials) before they sync. We never see this passphrase. Pick something memorable — there is no recovery without it.": "একটি সিঙ্ক পাসফ্রেজ আপনার সংবেদনশীল ক্ষেত্রগুলি (যেমন OPDS ক্যাটালগ শংসাপত্র) সিঙ্ক করার আগে এনক্রিপ্ট করে। আমরা কখনই এই পাসফ্রেজ দেখি না। এমন কিছু বেছে নিন যা মনে রাখা যায় — এটি ছাড়া পুনরুদ্ধার সম্ভব নয়।",
"Enter the sync passphrase you set on another device to decrypt your synced credentials.": "আপনার সিঙ্ক করা শংসাপত্রগুলি ডিক্রিপ্ট করতে অন্য একটি ডিভাইসে আপনি যে সিঙ্ক পাসফ্রেজ সেট করেছেন তা লিখুন।",
"Confirm passphrase": "পাসফ্রেজ নিশ্চিত করুন",
"Unlock": "আনলক করুন",
"Set PIN": "PIN সেট করুন",
"Change PIN": "PIN পরিবর্তন করুন",
"Disable PIN": "PIN নিষ্ক্রিয় করুন",
"Enter your current PIN, then choose a new 4-digit PIN.": "আপনার বর্তমান PIN লিখুন, তারপর একটি নতুন ৪-সংখ্যার PIN বেছে নিন।",
"Enter your current PIN to disable the app lock.": "অ্যাপ লক নিষ্ক্রিয় করতে আপনার বর্তমান PIN লিখুন।",
"PIN must be {{length}} digits": "PIN অবশ্যই {{length}} সংখ্যার হতে হবে",
"PINs do not match": "PIN মিলছে না",
"Current PIN": "বর্তমান PIN",
"New PIN": "নতুন PIN",
"Confirm new PIN": "নতুন PIN নিশ্চিত করুন",
"Manage Sync": "সিঙ্ক ব্যবস্থাপনা",
"Imported book files and library metadata": "আমদানি করা বইয়ের ফাইল ও লাইব্রেরির মেটাডেটা",
"Last-read position, bookmarks, and per-book preferences": "শেষ পঠিত অবস্থান, বুকমার্ক এবং প্রতি-বই পছন্দসমূহ",
"Highlights and notes": "হাইলাইট এবং নোট",
"Fonts": "ফন্ট",
"Custom font files": "কাস্টম ফন্ট ফাইল",
"Backgrounds": "পটভূমিসমূহ",
"Custom background textures": "কাস্টম পটভূমির টেক্সচার",
"OPDS catalogs": "OPDS ক্যাটালগ",
"Saved catalog URLs and (encrypted) credentials": "সংরক্ষিত ক্যাটালগ URL এবং (এনক্রিপ্ট করা) ক্রেডেনশিয়াল",
"Wrong sync passphrase — synced credentials could not be decrypted": "ভুল সিঙ্ক পাসফ্রেজ — সিঙ্ক করা ক্রেডেনশিয়াল ডিক্রিপ্ট করা যায়নি",
"Sync passphrase data on the server was reset. Re-encrypting your credentials under the new passphrase…": "সার্ভারের সিঙ্ক পাসফ্রেজ ডেটা রিসেট করা হয়েছে। নতুন পাসফ্রেজ দিয়ে আপনার ক্রেডেনশিয়াল পুনরায় এনক্রিপ্ট করা হচ্ছে…",
"Failed to decrypt synced credentials": "সিঙ্ক করা ক্রেডেনশিয়াল ডিক্রিপ্ট করতে ব্যর্থ",
"Imported dictionary bundles and settings": "আমদানি করা অভিধান বান্ডিল এবং সেটিংস",
"Choose what syncs across your devices. Disabling a category stops this device from sending or receiving rows of that kind. Anything already on the server is left alone, re-enabling resumes from where you stopped.": "আপনার ডিভাইসগুলোর মধ্যে কী সিঙ্ক হবে তা বেছে নিন। কোনো বিভাগ নিষ্ক্রিয় করলে এই ডিভাইসটি সেই ধরনের সারি পাঠানো বা গ্রহণ করা বন্ধ করে দেয়। সার্ভারে ইতিমধ্যে থাকা কিছুই পরিবর্তন হয় না, পুনরায় সক্রিয় করলে যেখানে থেমেছিলেন সেখান থেকে আবার শুরু হয়।",
"Data Sync": "ডেটা সিঙ্ক",
"Manage Fonts": "ফন্ট পরিচালনা",
"App settings": "অ্যাপ সেটিংস",
"Theme, highlight colours, integrations (KOSync, Readwise, Hardcover), and dictionary order": "থিম, হাইলাইট রং, ইন্টিগ্রেশন (KOSync, Readwise, Hardcover) এবং অভিধানের ক্রম",
"Required while Dictionaries sync is enabled": "অভিধান সিঙ্ক সক্রিয় থাকাকালীন প্রয়োজনীয়",
"Resets in {{hours}} hr {{minutes}} min": "{{hours}} ঘ {{minutes}} মি পরে রিসেট হবে",
"Pick a 4-digit PIN. You will need to enter it every time you open Readest. There is no way to recover a forgotten PIN. Clearing the app data is the only way to reset it.": "একটি ৪-অঙ্কের পিন বেছে নিন। প্রতিবার Readest খোলার সময় আপনাকে এটি প্রবেশ করাতে হবে। ভুলে যাওয়া পিন পুনরুদ্ধারের কোনো উপায় নেই। অ্যাপের ডেটা মুছে ফেলাই এটি রিসেট করার একমাত্র উপায়।",
"Credentials": "ক্রেডেনশিয়াল",
"Tokens, usernames, and passwords for OPDS, KOReader, Hardcover, and Readwise. When disabled, credentials remain on this device only and are never uploaded.": "OPDS, KOReader, Hardcover এবং Readwise-এর জন্য টোকেন, ব্যবহারকারীর নাম এবং পাসওয়ার্ড। নিষ্ক্রিয় থাকলে ক্রেডেনশিয়ালগুলি কেবল এই ডিভাইসেই থাকে এবং কখনই আপলোড হয় না।",
"Sensitive synced fields are encrypted before upload. Set a passphrase now or later when encryption is needed.": "সংবেদনশীল সিঙ্ক করা ক্ষেত্রগুলি আপলোডের আগে এনক্রিপ্ট করা হয়। এনক্রিপশনের প্রয়োজন হলে এখনই অথবা পরে একটি পাসফ্রেজ সেট করুন।",
"Saved to this account. You will be prompted for it when decrypting credentials.": "এই অ্যাকাউন্টে সংরক্ষিত। ক্রেডেনশিয়াল ডিক্রিপ্ট করার সময় আপনাকে এটি দিতে বলা হবে।",
"Reading progress on this device differs from \"{{deviceName}}\".": "এই ডিভাইসে পড়ার অগ্রগতি \"{{deviceName}}\" থেকে আলাদা।",
"This device": "এই ডিভাইস",
"Red": "লাল",
"Yellow": "হলুদ",
"Green": "সবুজ",
"Blue": "নীল",
"Violet": "বেগুনি",
"Edit color": "রঙ সম্পাদনা করুন",
"Add custom color": "কাস্টম রঙ যোগ করুন",
"Add label": "লেবেল যোগ করুন",
"Choose color": "রঙ নির্বাচন করুন",
"Connected to Hardcover. Last synced {{time}}.": "Hardcover-এর সাথে সংযুক্ত। সর্বশেষ সিঙ্ক {{time}}।",
"Integrations": "ইন্টিগ্রেশন",
"Hardcover": "Hardcover",
"Connected to Readwise. Last synced {{time}}.": "Readwise-এর সাথে সংযুক্ত। সর্বশেষ সিঙ্ক {{time}}।",
"Readwise": "Readwise",
"Browse and download books from online catalogs.": "অনলাইন ক্যাটালগ থেকে বই ব্রাউজ এবং ডাউনলোড করুন।",
"Connected as {{user}}": "{{user}} হিসাবে সংযুক্ত",
"Not connected": "সংযুক্ত নয়",
"{{count}} catalog_one": "{{count}}টি ক্যাটালগ",
"{{count}} catalog_other": "{{count}}টি ক্যাটালগ",
"No catalogs": "কোনো ক্যাটালগ নেই",
"Connect Readest to external services for sync, highlights, and catalogs.": "সিঙ্ক, হাইলাইট এবং ক্যাটালগের জন্য Readest-কে বাহ্যিক পরিষেবার সাথে সংযুক্ত করুন।",
"Reading Sync": "পড়ার সিঙ্ক",
"Content Sources": "কন্টেন্ট উৎস",
"Scroll tabs": "ট্যাব স্ক্রোল করুন",
"Catalog actions": "ক্যাটালগ অ্যাকশন",
"File Content": "ফাইল সামগ্রী",
"Start your library": "আপনার লাইব্রেরি শুরু করুন",
"Pick a book from your device to add it to your library.": "আপনার লাইব্রেরিতে যোগ করতে ডিভাইস থেকে একটি বই বেছে নিন।",
"Drop a book anywhere on this window, or pick one from your computer.": "এই উইন্ডোর যেকোনো জায়গায় একটি বই ছাড়ুন, অথবা আপনার কম্পিউটার থেকে একটি বেছে নিন।",
"Sign in to sync your library": "আপনার লাইব্রেরি সিঙ্ক করতে সাইন ইন করুন"
}
@@ -102,11 +102,9 @@
"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": "དཔེ་དེབ་འདིར་སྤྱོད་པ།",
@@ -173,9 +171,6 @@
"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": "སྡེ་ཁུལ་གྱི་རྒྱབ་སྐྱོར།",
@@ -188,7 +183,6 @@
"RTL Direction": "གཡས་ནས་གཡོན་དུ།",
"Maximum Column Height": "གྲལ་ཐིག་མཐོ་ཤོས།",
"Maximum Column Width": "གྲལ་ཐིག་རྒྱ་ཆེ་ཤོས།",
"Continuous Scroll": "རྒྱུན་མཐུད་འཁོར་རྒྱུག",
"Fullscreen": "བརྡ་ཁྱབ་ངོས་ཆ་ཚང་།",
"No supported files found. Supported formats: {{formats}}": "རྒྱབ་སྐྱོར་བྱེད་པའི་ཡིག་ཆ་མ་རྙེད། རྒྱབ་སྐྱོར་བྱེད་པའི་རྣམ་པ། {{formats}}",
"Drop to Import Books": "འདྲུད་འཇོག་བྱས་ནས་དཔེ་དེབ་ནང་འདྲེན།",
@@ -301,7 +295,6 @@
"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}} སྐར་མ་ལྷག་ཡོད།",
@@ -334,10 +327,10 @@
"Fit": "མཐུན་སྒྲིག",
"Reset {{settings}}": "{{settings}} བསྐྱར་སྒྲིག",
"Reset Settings": "སྒྲིག་བཀོད་བསྐྱར་སྒྲིག",
"{{count}} pages left in chapter_other": "ལེའུ་འདིར་ད་དུང་ {{count}} ཤོག་ལྷེ་ལྷག་ཡོད།",
"{{count}} pages left in chapter_other": "<1>ལེའུ་འདིར་ད་དུང་ </1><0>{{count}}</0><1> ཤོག་ལྷེ་ལྷག་ཡོད།</1>",
"Show Remaining Pages": "ལྷག་མའི་ཤོག་ལྷེའི་གྲངས་ཀ་མངོན་པ།",
"Source Han Serif CN VF": "སི་ཡོན་སུང་ཐི།",
"Huiwen-mincho": "ཧུའེ་ཝུན་མིང་ཁྲའོ་ཐི།",
"Source Han Serif CN": "སི་ཡོན་སུང་ཐི།",
"Huiwen-MinchoGBK": "ཧུའེ་ཝུན་མིང་ཁྲའོ་ཐི།",
"KingHwa_OldSong": "ཅིང་ཧྭ་ལའོ་སུང་ཐི།",
"Manage Subscription": "མཚམས་སྦྱོར་དོ་དམ།",
"Coming Soon": "ཉེ་བར་སྤྱོད་འགོ་འཛུགས་རྒྱུ།",
@@ -462,7 +455,6 @@
"Remove from Device Only": "སྒྲིག་ཆས་ཐོག་ནས་ཕྱིར་འཐེན།",
"Failed to connect": "འབྲེལ་བ་བྱས་མ་ཐུབ།",
"Disconnected": "འབྲེལ་བ་བྱས་མ་ཐུབ།",
"KOReader Sync Settings": "KOReader སྤྲི་དོན་གཞི་འདེམས་པ།",
"Sync as {{userDisplayName}}": "ཁྱེད་ཀྱིས {{userDisplayName}} སྤྲི་དོན་གཞི་འདེམས་པ།",
"Sync Server Connected": "སྤྲི་དོན་སང་བཞིན་འདེམས་པ།",
"Sync Strategy": "སྤྲི་དོན་འབྱུང་ཁུངས།",
@@ -471,7 +463,6 @@
"Send changes only": "འབྱུང་ཁུངས་འདེམས་པ་བྱས་ནས་བརྗེ་བ།",
"Receive changes only": "འབྱུང་ཁུངས་འདེམས་པ་བྱས་ནས་བརྗེ་བ།",
"Checksum Method": "ཡིག་སྣེ་འབྱུང་ཁུངས།",
"File Content (recommended)": "ཡིག་སྣེ་འབྱུང་ཁུངས།",
"File Name": "ཡིག་སྣེ་འབྱུང་ཁུངས།",
"Device Name": "ཡིག་སྣེ་འབྱུང་ཁུངས།",
"Connect to your KOReader Sync server.": "ཁྱེད་ཀྱིས KOReader སྤྲི་དོན་སང་བཞིན་འདེམས་པ།",
@@ -482,10 +473,7 @@
"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": "ད་ལྟའི་གནས་ས།",
@@ -598,11 +586,10 @@
"Size": "ཨང་",
"Cover": "དེབ་གྱི་སྤྱོད་བྱས་མ་ཐུབ།",
"Contain": "འབྱོར་བ།",
"{{number}} pages left in chapter": "དོན་ཚན་ནང་ཤོག་ཨང་ {{number}} ལོག་གི་འདུག།",
"{{number}} pages left in chapter": "<1>དོན་ཚན་ནང་ཤོག་ཨང་ </1><0>{{number}}</0><1> ལོག་གི་འདུག།</1>",
"Device": "རྐྱབ་སྐོར",
"E-Ink Mode": "ཡིག་སྒྱུར་སྤོ་བ",
"Highlight Colors": "མདོག་ཚད་ཀྱི་བསྡུར་བ།",
"Auto Screen Brightness": "རྒྱབ་སྐོར་གྱི་འོད་ཟེར་རང་འགུལ།",
"Pagination": "ཤོག་གདོང་།",
"Disable Double Tap": "བརྒྱབ་གདོང་གཉིས་མ་འགྱོད།",
"Tap to Paginate": "ཤོག་གདོང་ལ་ཐོག་འགྲོ།",
@@ -637,7 +624,6 @@
"Strikethrough": "འཐེན་ཐིག",
"Squiggly": "འཁྱོག་ཐིག",
"Outline": "མཐའ་ཐིག",
"Save Current Color": "མིག་སྔའི་ཚོན་གཏན་འཁེལ་བྱེད།",
"Quick Colors": "མྱུར་ཚོན།",
"Highlighter": "མདོག་ཚད་ཀྱི་བསྡུར་བ།",
"Save Book Cover": "དེབ་གནས་བཅས་པའི་སྤྱོད་བྱས་མ་ཐུབ།",
@@ -695,9 +681,7 @@
"Validating...": "བདེན་སྦྱོར་བཞིན་...",
"View All": "ཡོངས་ལྟ་བ་",
"Forward": "མདུན་དུ་",
"OPDS Catalog": "OPDS དཀར་ཆག",
"Home": "གཙོ་ངོས་",
"Library": "དེབ་མཛོད་",
"{{count}} items_other": "{{count}} རྣམ་གྲངས་",
"Download completed": "ཕབ་ལེན་རྫོགས་སོང་།",
"Download failed": "ཕབ་ལེན་ཕམ་པ་",
@@ -716,5 +700,692 @@
"Last": "མཐའ་མ།",
"Cannot Load Page": "ཤོག་ངོས་འགུལ་སྐྱོང་བྱེད་ཐུབ་མེད།",
"An error occurred": "ནོར་འཁྲུལ་ཞིག་བྱུང་སོང་།",
"Online Library": "དྲ་རྒྱུན་དེབ་མཛོད།"
"Online Library": "དྲ་རྒྱུན་དེབ་མཛོད།",
"URL must start with http:// or https://": "URL ནི་ http:// ཡང་ https:// ནས་འགོ་བཙུགས་དགོ།",
"Title, Author, Tag, etc...": "མིང་།, རྩོམ་པ།, མཚོན་འགྲེལ།, དེ་ལས་སྐུགས་...",
"Query": "འཚོལ་ཞིབ་",
"Subject": "དོན་ཚན་",
"Enter {{terms}}": "{{terms}} ལ་འགྲོ།",
"No search results found": "འཚོལ་ཞིབ་རྫོགས་མ་ཐུབ།",
"Failed to load OPDS feed: {{status}} {{statusText}}": "OPDS འཕྲིན་འདེམས་བྱས་མ་ཐུབ།: {{status}} {{statusText}}",
"Search in {{title}}": "{{title}} ནང་འཚོལ།",
"Manage Storage": "སྣོད་གསོག་དོ་དམ་བྱེད་པ",
"Failed to load files": "ཡིག་ཆ་སྣོན་པ་ཕམ་པ",
"Deleted {{count}} file(s)_other": "ཡིག་ཆ་ {{count}} བསུབས་ཟིན་པ",
"Failed to delete {{count}} file(s)_other": "ཡིག་ཆ་ {{count}} བསུབས་པ་ཕམ་པ",
"Failed to delete files": "ཡིག་ཆ་བསུབས་པ་ཕམ་པ",
"Total Files": "ཡིག་ཆ་ཡོངས་བསྡོམས",
"Total Size": "ཆེས་ཆེར་ཆེ་ཆུང",
"Quota": "ཁུལ་ཚད",
"Used": "ལག་ལེན་བྱས་ཟིན་པ",
"Files": "ཡིག་ཆ",
"Search files...": "ཡིག་ཆ་འཚོལ...",
"Newest First": "གསར་ཤོས་སྔོན་དུ",
"Oldest First": "རྙིང་ཤོས་སྔོན་དུ",
"Largest First": "ཆེ་ཤོས་སྔོན་དུ",
"Smallest First": "ཆུང་ཤོས་སྔོན་དུ",
"Name A-Z": "མིང་ A-Z",
"Name Z-A": "མིང་ Z-A",
"{{count}} selected_other": "{{count}} ཡིག་ཆ་འདེམས་ཟིན་པ",
"Delete Selected": "འདེམས་པ་བསུབས་པ",
"Created": "སྤེལ་བྱས་ཟིན་པ",
"No files found": "ཡིག་ཆ་མ་རྙེད་པ",
"No files uploaded yet": "ད་ཚུན་ཡིག་ཆ་སྣོན་མི་འདུག",
"files": "ཡིག་ཆ",
"Page {{current}} of {{total}}": "ཤོག་ངོས་ {{total}} ནས་ {{current}}",
"Are you sure to delete {{count}} selected file(s)?_other": "ཁྱེད་ཀྱིས་འདེམས་པའི་ཡིག་ཆ་ {{count}} བསུབས་དགོས་པ་ངེས་ཡིན་ན?",
"Cloud Storage Usage": "སྤྲིན་གནས་སྣོད་གསོག་ལུས་སྐོར།",
"Rename Group": "ཚོགས་མིང་བསྒྱུར་བ།",
"From Directory": "སྐོར་འདེམས་པ་ནས།",
"Successfully imported {{count}} book(s)_other": "སྤྲིན་ནས་ཕབ་སྟེ་འབེབས་ {{count}} དེབ་འདེམས་སོང་",
"Count": "ཨང་",
"Start Page": "ཤོག་ངོས་དང་པོ",
"Search in OPDS Catalog...": "OPDS དཀར་ཆག་ནང་འཚོལ།...",
"Please log in to use advanced TTS features": "དབང་བསྐྱོད་ཀྱི TTS རྣམ་པ་ཚུགས་སྤྱོད་བྱས་མ་ཐུབ།",
"Word limit of 30 words exceeded.": "ཚིག་གཅིག 30 ལྟར་འདོད་མེད།",
"Proofread": "མིག་སྔའི་བཤད་རྒྱུན།",
"Current selection": "དེབ་གནས་བཅས་པའི་སྤྱོད་བྱས་མ་ཐུབ།",
"All occurrences in this book": "དེབ་ནང་གི་འདི་ཚུགས་ཀྱི་འཚོལ་ཞིབ།",
"All occurrences in your library": "ཁྱོད་ཀྱི་དེབ་མཛོད་ནང་གི་འདི་ཚུགས་ཀྱི་འཚོལ་ཞིབ།",
"Selected text:": "དེབ་གནས་བཅས་པའི་སྤྱོད་བྱས་མ་ཐུབ།",
"Replace with:": "བརྗེ་བ།:",
"Enter text...": "ཡིག་ཆ་ལ་འགྲོ།...",
"Case sensitive:": "གནས་སྟངས་འདི་ལ་འབད་དགོས།",
"Scope:": "གནས་ཚུལ།:",
"Selection": "དེབ་གནས་བཅས་པའི་སྤྱོད་བྱས་མ་ཐུབ།",
"Library": "དེབ་མཛོད།",
"Yes": "ཨིན་",
"No": "མེད་",
"Proofread Replacement Rules": "མིག་སྔའི་བཤད་རྒྱུན་བརྗེ་བའི་རྣམ་པ།",
"Selected Text Rules": "བདམས་པའི་ཡིག་ཆ་བརྗེ་སྒྱུར་སྲིད་སྒྲིག",
"No selected text replacement rules": "བདམས་པའི་ཡིག་ཆ་བརྗེ་སྒྱུར་སྲིད་སྒྲིག་མེད་པ",
"Book Specific Rules": "དེབ་སྒོས་ཀྱི་བརྗེ་སྒྱུར་སྲིད་སྒྲིག",
"No book-level replacement rules": "དེབ་རིམ་གྱི་བརྗེ་སྒྱུར་སྲིད་སྒྲིག་མེད་པ",
"Disable Quick Action": "མགྱོགས་མྱུར་བྱ་བ་སྤང་",
"Enable Quick Action on Selection": "འདེམས་པའི་སྐབས་མགྱོགས་མྱུར་བྱ་བ་སྤྱོད་",
"None": "མེད་",
"Annotation Tools": "མཆན་འགྲེལ་ལག་ཆ་",
"Enable Quick Actions": "མགྱོགས་མྱུར་བྱ་བ་སྤྱོད་",
"Quick Action": "མགྱོགས་མྱུར་བྱ་བ་",
"Copy to Notebook": "ཟིན་དེབ་ནང་འདྲ་བཤུས་",
"Copy text after selection": "ཡིག་ཆ་འདེམས་པའི་ཐབས་ཀྱིས་འདྲ་བཤུས་",
"Highlight text after selection": "ཡིག་ཆ་འདེམས་པའི་ཐབས་ཀྱིས་མངོན་གསལ་བྱེད་",
"Annotate text after selection": "ཡིག་ཆ་འདེམས་པའི་ཐབས་ཀྱིས་མཆན་འགྲེལ་བྱེད་",
"Search text after selection": "ཡིག་ཆ་འདེམས་པའི་ཐབས་ཀྱིས་འཚོལ་ཞིབ་བྱེད་",
"Look up text in dictionary after selection": "ཡིག་ཆ་འདེམས་པའི་ཐབས་ཀྱིས་ཚིག་མཛོད་ནང་འཚོལ་",
"Translate text after selection": "ཡིག་ཆ་འདེམས་པའི་ཐབས་ཀྱིས་སྐད་བསྒྱུར་བྱེད་",
"Read text aloud after selection": "ཡིག་ཆ་འདེམས་པའི་ཐབས་ཀྱིས་སྐད་ཀྱིས་ཀློག་",
"Proofread text after selection": "ཡིག་ཆ་འདེམས་པའི་ཐབས་ཀྱིས་ཞིབ་བཤེར་བྱེད་",
"{{activeCount}} active, {{pendingCount}} pending": "{{activeCount}} བྱེད་བཞིན་པ་, {{pendingCount}} སྒུག་བཞིན་པ་",
"{{failedCount}} failed": "{{failedCount}} ཕམ་པ་",
"Waiting...": "སྒུག་བཞིན་པ་...",
"Failed": "ཕམ་པ་",
"Completed": "གྲུབ་སོང་",
"Cancelled": "དོར་སོང་",
"Retry": "བསྐྱར་ཚོད་",
"Active": "བྱེད་བཞིན་པ་",
"Transfer Queue": "སྐྱེལ་འདྲེན་སྒྲིག་གཅོད་",
"Upload All": "ཚང་མ་འཇོག་",
"Download All": "ཚང་མ་ལེན་",
"Resume Transfers": "སྐྱེལ་འདྲེན་མུ་མཐུད་",
"Pause Transfers": "སྐྱེལ་འདྲེན་མཚམས་འཇོག་",
"Pending": "སྒུག་བཞིན་པ་",
"No transfers": "སྐྱེལ་འདྲེན་མེད་",
"Retry All": "ཚང་མ་བསྐྱར་ཚོད་",
"Clear Completed": "གྲུབ་པ་གསལ་བ་",
"Clear Failed": "ཕམ་པ་གསལ་བ་",
"Upload queued: {{title}}": "འཇོག་པའི་སྒྲིག་གཅོད་ནང་བཅུག་: {{title}}",
"Download queued: {{title}}": "ལེན་པའི་སྒྲིག་གཅོད་ནང་བཅུག་: {{title}}",
"Book not found in library": "དཔེ་མཛོད་ནང་དཔེ་ཆ་རྙེད་མ་སོང་",
"Unknown error": "མ་ཤེས་པའི་ནོར་འཁྲུལ་",
"Please log in to continue": "མུ་མཐུད་པར་ནང་འཛུལ་བྱོས་",
"Cloud File Transfers": "སྤྲིན་གནས་ཡིག་ཆ་སྐྱེལ་འདྲེན།",
"Show Search Results": "འཚོལ་བཤེར་འབྲས་བུ་སྟོན།",
"Search results for '{{term}}'": "'{{term}}'ཡི་འབྲས་བུ།",
"Close Search": "འཚོལ་བཤེར་སྒོ་རྒྱག",
"Previous Result": "སྔོན་མའི་འབྲས་བུ།",
"Next Result": "རྗེས་མའི་འབྲས་བུ།",
"Bookmarks": "དཔེ་རྟགས།",
"Annotations": "མཆན།",
"Show Results": "འབྲས་བུ་སྟོན།",
"Clear search": "འཚོལ་བཤེར་གཙང་སེལ།",
"Clear search history": "འཚོལ་བཤེར་ལོ་རྒྱུས་གཙང་སེལ།",
"Tap to Toggle Footer": "ཞབས་མཇུག་སྒོ་འབྱེད་བྱེད་པར་གནོན།",
"Show Current Time": "ད་ལྟའི་དུས་ཚོད་སྟོན་པ།",
"Use 24 Hour Clock": "ཆུ་ཚོད་ ༢༤ ཅན་གྱི་ཟློས་འཁོར་བཀོལ་བ།",
"Show Current Battery Status": "ད་ལྟའི་གློག་རྫས་གནས་བབ་སྟོན་པ།",
"Exported successfully": "ཕྱིར་འདོན་ལེགས་གྲུབ་བྱུང་།",
"Book exported successfully.": "དཔེ་དེབ་ཕྱིར་འདོན་ལེགས་གྲུབ་བྱུང་།",
"Failed to export the book.": "དཔེ་དེབ་ཕྱིར་འདོན་མི་ཐུབ།",
"Export Book": "དཔེ་དེབ་ཕྱིར་འདོན།",
"Whole word:": "ཚིག་གྲུབ་ཆ་ཚང་:",
"Date Published": "པར་སྐྲུན་ཚེས་གྲངས།",
"Only for TTS:": "TTS ལ་ཁོ་ན།:",
"Uploaded": "ཡར་སྐྱེལ་བྱས་ཟིན།",
"Downloaded": "མར་ལེན་བྱས་ཟིན།",
"Deleted": "བསུབས་ཟིན།",
"Note:": "གསལ་བཤད།:",
"Time:": "དུས་ཚོད།:",
"Format Options": "རྣམ་གཞག་གདམ་ག",
"Export Date": "ཕྱིར་འདོན་ཚེས་གྲངས།",
"Chapter Titles": "ལེའུའི་མཚན།",
"Chapter Separator": "ལེའུའི་དབར་མཚམས།",
"Highlights": "གཙོ་གནད།",
"Note Date": "གསལ་བཤད་ཚེས་གྲངས།",
"Advanced": "མཐོ་རིམ།",
"Hide": "སྦས་པ།",
"Show": "མངོན་པ།",
"Use Custom Template": "རང་བཟོའི་དཔེ་གཞི་བེད་སྤྱོད།",
"Export Template": "ཕྱིར་འདོན་དཔེ་གཞི།",
"Template Syntax:": "དཔེ་གཞིའི་སྒྲིག་གཞི།:",
"Insert value": "གྲངས་ཐང་བཙུགས་པ།",
"Format date (locale)": "ཚེས་གྲངས་རྣམ་གཞག (ས་གནས།)",
"Format date (custom)": "ཚེས་གྲངས་རྣམ་གཞག (རང་བཟོ།)",
"Conditional": "དམིགས་བསལ།",
"Loop": "འཁོར་ལོ།",
"Available Variables:": "བེད་སྤྱད་རུང་བའི་འགྱུར་ཅན།:",
"Book title": "དེབ་མཚན།",
"Book author": "དེབ་རྩོམ་པ་པོ།",
"Export date": "ཕྱིར་འདོན་ཚེས་གྲངས།",
"Array of chapters": "ལེའུའི་ལེབ་ངོས།",
"Chapter title": "ལེའུའི་མཚན།",
"Array of annotations": "གསལ་བཤད་ལེབ་ངོས།",
"Highlighted text": "གཙོ་གནད་ཡིག་རྐྱང་།",
"Annotation note": "གསལ་བཤད་ཟིན་བྲིས།",
"Date Format Tokens:": "ཚེས་གྲངས་རྣམ་གཞག་རྟགས།:",
"Year (4 digits)": "ལོ། (གྲངས་ཐང་4)",
"Month (01-12)": "ཟླ། (01-12)",
"Day (01-31)": "ཚེས། (01-31)",
"Hour (00-23)": "ཆུ་ཚོད། (00-23)",
"Minute (00-59)": "སྐར་མ། (00-59)",
"Second (00-59)": "སྐར་ཆ། (00-59)",
"Show Source": "འབྱུང་ཁུངས་མངོན་པ།",
"No content to preview": "སྔོན་ལྟ་བྱེད་པའི་དོན་རྐྱེན་མེད།",
"Export": "ཕྱིར་འདོན།",
"Set Timeout": "དུས་ཚོད་སྒྲིག་པ།",
"Select Voice": "སྐད་གདངས་འདེམས།",
"Toggle Sticky Bottom TTS Bar": "TTS སྡོམ་ཐིག་བརྗེ་བ།",
"Display what I'm reading on Discord": "Discord ཐོག་ཀློག་བཞིན་པའི་དཔེ་ཆ་སྟོན།",
"Show on Discord": "Discord ཐོག་སྟོན།",
"Instant {{action}}": "འཕྲལ་མར་{{action}}",
"Instant {{action}} Disabled": "འཕྲལ་མར་{{action}}་ལྕོགས་མིན་བཟོས།",
"Annotation": "མཆན་འགྲེལ།",
"Reset Template": "དཔེ་གཞི་བསྐྱར་སྒྲིག",
"Annotation style": "མཆན་འགྲེལ་གྱི་བཟོ་ལྟ།",
"Annotation color": "མཆན་འགྲེལ་གྱི་ཚོན་མདོག",
"Annotation time": "མཆན་འགྲེལ་གྱི་དུས་ཚོད།",
"AI": "རིག་ནུས་མིས་བཟོས། (AI)",
"Are you sure you want to re-index this book?": "ཁྱེད་ཀྱིས་དཔེ་དེབ་འདི་བསྐྱར་དུ་དཀར་ཆག་བཟོ་རྒྱུ་གཏན་འཁེལ་ཡིན་ནམ།",
"Enable AI in Settings": "སྒྲིག་བཀོད་ནང་ AI སྤྱོད་པར་བྱོས།",
"Index This Book": "དཔེ་དེབ་འདི་དཀར་ཆག་བཟོ་བ།",
"Enable AI search and chat for this book": "དཔེ་དེབ་འདིར་ AI འཚོལ་བཤེར་དང་ཁ་བརྡ་སྤྱོད་པར་བྱོས།",
"Start Indexing": "དཀར་ཆག་བཟོ་འགོ་བཙུགས་པ།",
"Indexing book...": "དཔེ་དེབ་དཀར་ཆག་བཟོ་བཞིན་པ།...",
"Preparing...": "གྲ་སྒྲིག་བྱེད་བཞིན་པ།...",
"Delete this conversation?": "ཁ་བརྡ་འདི་བསུབ་དགོས་སམ།",
"No conversations yet": "ད་ལྟའི་བར་ཁ་བརྡ་མེད།",
"Start a new chat to ask questions about this book": "དཔེ་དེབ་འདིའི་སྐོར་ལ་དྲི་བ་དྲི་བར་ཁ་བརྡ་གསར་པ་ཞིག་འགོ་བཙུགས་པ།",
"Rename": "མིང་བརྗེ་བ།",
"New Chat": "ཁ་བརྡ་གསར་པ།",
"Chat": "ཁ་བརྡ།",
"Please enter a model ID": "དཔེ་གཞིའི་ཨང་རྟགས་ནང་འཇུག་བྱོས།",
"Model not available or invalid": "དཔེ་གཞི་མེད་པ་འམ་ནུས་མེད།",
"Failed to validate model": "དཔེ་གཞི་བདེན་སྦྱོར་བྱེད་ཐུབ་མེད།",
"Couldn't connect to Ollama. Is it running?": "Ollama ལ་འབྲེལ་མཐུད་བྱེད་ཐུབ་མེད། དེ་འཁོར་བཞིན་ཡོད་དམ།",
"Invalid API key or connection failed": "API ལྡེ་མིག་ནུས་མེད་དམ་འབྲེལ་མཐུད་ཕམ་པ།",
"Connection failed": "འབྲེལ་མཐུད་ཕམ་པ།",
"AI Assistant": "AI རོགས་པ།",
"Enable AI Assistant": "AI རོགས་པ་སྤྱོད་པར་བྱོས།",
"Provider": "མཁོ་འདོན་པ།",
"Ollama (Local)": "Ollama (ས་གནས།)",
"AI Gateway (Cloud)": "AI འཛུལ་སྒོ། (སྤྲིན་གནས།)",
"Ollama Configuration": "Ollama སྒྲིག་བཀོད།",
"Refresh Models": "དཔེ་གཞི་བསྐྱར་བརྗེ།",
"AI Model": "AI དཔེ་གཞི།",
"No models detected": "དཔེ་གཞི་མ་རྙེད།",
"AI Gateway Configuration": "AI འཛུལ་སྒོའི་སྒྲིག་བཀོད།",
"Choose from a selection of high-quality, economical AI models. You can also bring your own model by selecting \"Custom Model\" below.": "སྤུས་ལེགས་དང་ཁེ་ཕན་ཆེ་བའི་ AI དཔེ་གཞི་འདེམས་དགོས། ཁྱེད་ཀྱིས་འོག་གི་ \"རང་བཟོའི་དཔེ་གཞི།\" བདམས་ནས་རང་གི་དཔེ་གཞི་བེད་སྤྱོད་ཀྱང་ཆོག",
"API Key": "API ལྡེ་མིག་",
"Get Key": "ལྡེ་མིག་ལེན་པ།",
"Model": "དཔེ་གཞི།",
"Custom Model...": "རང་བཟོའི་དཔེ་གཞི།...",
"Custom Model ID": "རང་བཟོའི་དཔེ་གཞིའི་ཨང་རྟགས།",
"Validate": "བདེན་སྦྱོར།",
"Model available": "དཔེ་གཞི་ཡོད།",
"Connection": "འབྲེལ་མཐུད།",
"Test Connection": "འབྲེལ་མཐུད་ཚོད་ལྟ།",
"Connected": "འབྲེལ་མཐུད་ཟིན།",
"Custom Colors": "རང་བཟོས་ཚོན་མདོག",
"Color E-Ink Mode": "ཚོན་ལྡན་གློག་རྡུལ་སྣག་ཚའི་རྣམ་པ།",
"Reading Ruler": "ལྟ་ཀློག་ཐིག་ཤིང་།",
"Enable Reading Ruler": "ལྟ་ཀློག་ཐིག་ཤིང་སྤྱོད་པ།",
"Lines to Highlight": "མངོན་གསལ་དུ་གཏོང་དགོས་པའི་ཐིག་ཕྲེང་།",
"Ruler Color": "ཐིག་ཤིང་ගི་ཚོན་མདོག",
"Command Palette": "བཀོད་འདོམས་པང་ལེབ།",
"Search settings and actions...": "སྒྲིག་བཀོད་དང་བྱ་འགུལ་འཚོལ་བ།...",
"No results found for": "འབྲས་བུ་མ་རྙེད་པ།",
"Type to search settings and actions": "ཡིག་གཟུགས་ནང་འཇུག་བྱས་ཏེ་སྒྲིག་བཀོད་དང་བྱ་འགུལ་འཚོལ་བ།",
"Recent": "ཉེ་ཆར།",
"navigate": "འགྲུལ་བཞུད།",
"select": "འདེམས་པ།",
"close": "ཁ་རྒྱག་པ།",
"Search Settings": "སྒྲིག་བཀོད་འཚོལ་བ།",
"Page Margins": "ཤོག་ངོས་མཐའ་འགྲམ།",
"AI Provider": "ནུས་པའི་རིག་རྩལ་མཁོ་སྤྲོད་པ།",
"Ollama URL": "Ollama URL",
"Ollama Model": "Ollama དཔེ་གཞི།",
"AI Gateway Model": "AI Gateway དཔེ་གཞི།",
"Actions": "བྱ་འགུལ།",
"Navigation": "འགྲུལ་བཞུད།",
"Set status for {{count}} book(s)_other": "དེབ་ {{count}} གི་གནས་སྟངས་གཏན་འཁེལ་བྱེད།",
"Mark as Unread": "མ་ཀློག་པར་རྟགས་རྒྱག",
"Mark as Finished": "ཚར་བར་རྟགས་རྒྱག",
"Finished": "ཚར་སོང་།",
"Unread": "མ་ཀློག་པ།",
"Clear Status": "གནས་སྟངས་གཙང་མ།",
"Status": "གནས་སྟངས།",
"Loading": "ལེན་བཞིན་པ།...",
"Exit Paragraph Mode": "དུམ་མཚམས་རྣམ་པ་ནས་ཕྱིར་ཐོན།",
"Paragraph Mode": "དུམ་མཚམས་རྣམ་པ།",
"Embedding Model": "གནས་སྒྲིག་དཔེ་གཞི།",
"{{count}} book(s) synced_other": "དེབ་ {{count}} མཉམ་འགྲིག་བྱས་ཟིན།",
"Unable to start RSVP": "RSVP འགོ་འཛུགས་མ་ཐུབ།",
"RSVP not supported for PDF": "PDF ལ་ RSVP རྒྱབ་སྐྱོར་མེད།",
"Select Chapter": "ལེའུ་འདེམས་པ།",
"Context": "བརྗོད་དོན།",
"Ready": "གྲ་སྒྲིག་ཡོད།",
"Chapter Progress": "ལེའུའི་འཕེལ་རིམ།",
"words": "ཚིག",
"{{time}} left": "དུས་ཚོད་ {{time}} ལྷག་ཡོད།",
"Reading progress": "ཀློག་པའི་འཕེལ་རིམ།",
"Skip back 15 words": "ཚིག་ ༡༥ རྒྱབ་ལ་བཤུད་པ།",
"Back 15 words (Shift+Left)": "ཚིག་ ༡༥ རྒྱབ་ལ་བཤུད་པ། (Shift+Left)",
"Pause (Space)": "མཚམས་འཇོག་པ། (Space)",
"Play (Space)": "གཏོང་བ། (Space)",
"Skip forward 15 words": "ཚིག་ ༡༥ མདུན་ལ་བཤུད་པ།",
"Forward 15 words (Shift+Right)": "ཚིག་ ༡༥ མདུན་ལ་བཤུད་པ། (Shift+Right)",
"Decrease speed": "མགྱོགས་ཚད་འགོར་དུ་གཏོང་བ།",
"Slower (Left/Down)": "འགོར་བ། (Left/Down)",
"Increase speed": "མགྱོགས་ཚད་མྱུར་དུ་གཏོང་བ།",
"Faster (Right/Up)": "མྱུར་བ། (Right/Up)",
"Start RSVP Reading": "RSVP ཀློག་འགོ་འཛུགས་པ།",
"Choose where to start reading": "གང་ནས་ཀློག་འགོ་འཛུགས་མིན་འདེམས་པ།",
"From Chapter Start": "ལེའུའི་འགོ་ནས།",
"Start reading from the beginning of the chapter": "ལེའུ་འདིའི་འགོ་ནས་ཀློག་པ།",
"Resume": "མུ་མཐུད་པ།",
"Continue from where you left off": "མཚམས་བཞག་སའི་གནས་ནས་མུ་མཐུད་པ།",
"From Current Page": "ད་ལྟའི་ཤོག་ལྷེ་ནས།",
"Start from where you are currently reading": "ད་ལྟ་ཀློག་བཞིན་པའི་གནས་ནས་འགོ་འཛུགས་པ།",
"From Selection": "བདམས་པའི་ནང་དོན་ནས།",
"Speed Reading Mode": "མྱུར་ཀློག་རྣམ་པ།",
"Scroll left": "གཡོན་ལ་བཤུད་དགོས།",
"Scroll right": "གཡས་ལ་བཤུད་དགོས།",
"Library Sync Progress": "དཔེ་མཛོད་མཉམ་འབྱུང་གི་རིམ་པ།",
"Back to library": "དཔེ་མཛོད་ལ་ལོག་པ།",
"Group by...": "དབྱེ་བ་འབྱེད་སྟངས།...",
"Export as Plain Text": "ཡིག་རྐྱང་དཀྱུས་མར་ཕྱིར་འདྲེན།",
"Export as Markdown": "Markdown དུ་ཕྱིར་འདྲེན།",
"Show Page Navigation Buttons": "ཤོག་ངོས་མཐེབ་གནོན།",
"Page {{number}}": "ཤོག་ལྷེ། {{number}}",
"highlight": "འོད་རྟགས།",
"underline": "ཞབས་ཐིག",
"squiggly": "ཀྱག་ཀྱག་ཐིག",
"red": "དམར་པོ།",
"violet": "སྨུག་པོ།",
"blue": "སྔོན་པོ།",
"green": "ལྗང་ཁུ།",
"yellow": "སེར་པོ།",
"Select {{style}} style": "{{style}} བཟོ་ལྟ་བདམ་པ།",
"Select {{color}} color": "{{color}} ཚོན་མདོག་བདམ་པ།",
"Close Book": "དེབ་ཁ་རྒྱག་པ།",
"Speed Reading": "མགྱོགས་ཀློག",
"Close Speed Reading": "མགྱོགས་ཀློག་ཁ་རྒྱག་པ།",
"Authors": "རྩོམ་པ་པོ།",
"Books": "དཔེ་ཆ།",
"Groups": "ཚོགས་པ།",
"Back to TTS Location": "TTS གནས་སར་ཕྱིར་ལོག་པ།",
"Metadata": "གནད་སྨིན་གོ་དོན།",
"Image viewer": "པར་རིས་ལྟ་བྱེད།",
"Previous Image": "སྔོན་མའི་པར་རིས།",
"Next Image": "རྗེས་མའི་པར་རིས།",
"Zoomed": "ཆེར་བསྐྱེད་ཟིན།",
"Zoom level": "ཆེར་བསྐྱེད་རིམ་པ།",
"Table viewer": "རེའུ་མིག་ལྟ་བྱེད།",
"Unable to connect to Readwise. Please check your network connection.": "Readwise ལ་མཐུད་ཐུབ་མ་སོང་། ཁྱེད་ཀྱི་དྲ་རྒྱའི་མཐུད་ལམ་ལ་བརྟག་དཔྱད་གནང་རོགས།",
"Invalid Readwise access token": "Readwise འཛུལ་སྤྱོད་ལག་ཁྱེར་ནོར་འདུག",
"Disconnected from Readwise": "Readwise ནས་མཐུད་ལམ་བཅད་ཟིན།",
"Never": "ནམ་ཡང་མིན།",
"Sync Enabled": "མཉམ་བྱུང་ནུས་པ་སྤར་ཟིན།",
"Disconnect": "མཐུད་ལམ་གཅོད་པ།",
"Connect your Readwise account to sync highlights.": "ཁྱེད་ཀྱི་ Readwise རྩིས་ཐོ་མཐུད་ནས་བཀོད་མཆན་མཉམ་བྱུང་གནང་རོགས།",
"Get your access token at": "འཛུལ་སྤྱོད་ལག་ཁྱེར་འདི་ནས་ལེན་རོགས།",
"Access Token": "འཛུལ་སྤྱོད་ལག་ཁྱེེར།",
"Paste your Readwise access token": "Readwise འཛུལ་སྤྱོད་ལག་ཁྱེར་འདིར་སྦྱོར་རོགས།",
"Readwise Sync": "Readwise མཉམ་བྱུང་།",
"Push Highlights": "བཀོད་མཆན་སྐྱེལ་བ།",
"Highlights synced to Readwise": "བཀོད་མཆན་ Readwise ལ་མཉམ་བྱུང་བྱས་ཟིན།",
"Readwise sync failed: no internet connection": "Readwise མཉམ་བྱུང་མ་ཐུབ། དྲ་རྒྱའི་མཐུད་ལམ་མི་འདུག",
"Readwise sync failed: {{error}}": "Readwise མཉམ་བྱུང་མ་ཐུབ། {{error}}",
"System Screen Brightness": "མ་ལག་ཤེལ་སྒོའི་གསལ་ཚད།",
"Page:": "ཤོག་ལྷེ།:",
"Page: {{number}}": "ཤོག་ལྷེ།: {{number}}",
"Annotation page number": "མཆན་འགྲེལ་ཤོག་ཨང་།",
"Translating...": "ཡིག་བསྒྱུར་བྱེད་བཞིན་པ།...",
"Show Battery Percentage": "གློག་རྫས་བརྒྱ་ཆ་སྟོན།",
"Hide Scrollbar": "འགྲིལ་ཤིང་སྦེད་པ།",
"Skip to last reading position": "མཐའ་མའི་ཀློག་གནས་སུ་མཆོང་",
"Show context": "སྐབས་དོན་མངོན་པ",
"Hide context": "སྐབས་དོན་སྦས་པ",
"Decrease font size": "ཡིག་གཟུགས་ཆུང་དུ་གཏོང་བ",
"Increase font size": "ཡིག་གཟུགས་ཆེ་རུ་གཏོང་བ",
"Focus": "དམིགས་གཏད",
"Theme color": "བརྗོད་དོན་ཚོན་མདོག",
"Import failed": "ནང་འདྲེན་མི་ཐུབ།",
"Available Formatters:": "སྒྲིག་བཟོ་ཆས་ཡོད་པ།:",
"Format date": "ཚེས་གྲངས་སྒྲིག་བཟོ",
"Markdown block quote (> per line)": "Markdown དྲིལ་བསྡུ་འདྲེན་ (མི་ལྟར་ > )",
"Newlines to <br>": "གསར་བརྗེ་ <br> ལ་བསྒྱུར",
"Change case": "ཡིག་གཟུགས་བསྒྱུར",
"Trim whitespace": "སྟོང་ཆ་གཙང་བཟོ",
"Truncate to n characters": "n ཡི་གེར་ཐུང་དུ་བཅད",
"Replace text": "ཡི་གེ་བརྗེ་བ",
"Fallback value": "ཚབ་ཀྱི་གྲངས་ཀ",
"Get length": "རིང་ཚད་ལེན",
"First/last element": "དང་པོ/མཇུག་གི་རྒྱུ་ཆ",
"Join array": "ཚོ་སྒྲིག་སྦྲེལ",
"Backup failed: {{error}}": "གྲབས་ཉར་བྱེད་མ་ཐུབ: {{error}}",
"Select Backup": "གྲབས་ཉར་འདེམས་པ",
"Restore failed: {{error}}": "སླར་གསོ་བྱེད་མ་ཐུབ: {{error}}",
"Backup & Restore": "གྲབས་ཉར་དང་སླར་གསོ",
"Create a backup of your library or restore from a previous backup. Restoring will merge with your current library.": "ཁྱོད་ཀྱི་དཔེ་མཛོད་ཀྱི་གྲབས་ཉར་བཟོ་བའམ་སྔོན་གྱི་གྲབས་ཉར་ནས་སླར་གསོ་བྱོས། སླར་གསོ་བྱས་ན་ཁྱོད་ཀྱི་ད་ལྟའི་དཔེ་མཛོད་དང་སྤྲོད་རེས་བྱེད།",
"Backup Library": "དཔེ་མཛོད་གྲབས་ཉར",
"Restore Library": "དཔེ་མཛོད་སླར་གསོ",
"Creating backup...": "གྲབས་ཉར་བཟོ་བཞིན་པ...",
"Restoring library...": "དཔེ་མཛོད་སླར་གསོ་བྱེད་བཞིན་པ...",
"{{current}} of {{total}} items": "{{current}} / {{total}} རྣམ་གྲངས",
"Backup completed successfully!": "གྲབས་ཉར་ལེགས་གྲུབ་བྱུང་!",
"Restore completed successfully!": "སླར་གསོ་ལེགས་གྲུབ་བྱུང་!",
"Your library has been saved to the selected location.": "ཁྱོད་ཀྱི་དཔེ་མཛོད་འདེམས་ས་དེར་ཉར་ཟིན།",
"{{added}} books added, {{updated}} books updated.": "དཔེ་དེབ {{added}} བསྣན་ཟིན, {{updated}} གསར་བསྒྱུར་བྱས་ཟིན།",
"Operation failed": "བཀོལ་སྤྱོད་བྱེད་མ་ཐུབ",
"Loading library...": "དཔེ་མཛོད་འཇུག་བཞིན་པ...",
"{{count}} books refreshed_other": "تم تحديث {{count}} كتب",
"Failed to refresh metadata": "མེ་ཊ་ཌེ་ཊ་གསར་བསྒྱུར་བྱེད་མ་ཐུབ",
"Refresh Metadata": "མེ་ཊ་ཌེ་ཊ་གསར་བསྒྱུར",
"Keyboard Shortcuts": "མཐེབ་གཞོང་མྱུར་ལམ།",
"View all keyboard shortcuts": "མཐེབ་གཞོང་མྱུར་ལམ་ཚང་མ་ལྟ་བ།",
"Switch Sidebar Tab": "ཟུར་འབར་གྱི་ཤོག་བྱང་བརྗེ་བ།",
"Toggle Notebook": "ཟིན་དེབ་སྟོན་པ།/སྦས་པ།",
"Search in Book": "དཔེ་དེབ་ནང་འཚོལ་བ།",
"Toggle Scroll Mode": "འཁྱོག་སྒུལ་རྣམ་པ་བརྗེ་བ།",
"Toggle Select Mode": "འདེམས་རྣམ་པ་བརྗེ་བ།",
"Toggle Bookmark": "ཤོག་རྟགས་བརྗེ་བ།",
"Toggle Text to Speech": "ཡི་གེ་སྒྲ་ལ་བརྗེ་བ།",
"Play / Pause TTS": "སྒྲ་ཀློག་འགོ་འཛུགས / མཚམས་འཇོག",
"Toggle Paragraph Mode": "དུམ་བུ་རྣམ་པ་བརྗེ་བ།",
"Highlight Selection": "བདམས་པ་གསལ་བཀོད།",
"Underline Selection": "བདམས་པའི་འོག་ཐིག",
"Annotate Selection": "བདམས་པར་མཆན་འགོད།",
"Search Selection": "བདམས་པ་འཚོལ་བ།",
"Copy Selection": "བདམས་པ་བཤུ་བ།",
"Translate Selection": "བདམས་པ་ཡིག་སྒྱུར།",
"Dictionary Lookup": "ཚིག་མཛོད་ནང་འཚོལ་བ།",
"Read Aloud Selection": "བདམས་པ་སྒྲ་ཆེན་པོས་ཀློག",
"Proofread Selection": "བདམས་པ་ཞུ་དག",
"Open Settings": "སྒྲིག་འགོད་ཁ་ཕྱེ་བ།",
"Open Command Palette": "བཀའ་བརྡའི་པང་ཁ་ཕྱེ་བ།",
"Show Keyboard Shortcuts": "མཐེབ་གཞོང་མྱུར་ལམ་སྟོན་པ།",
"Open Books": "དཔེ་དེབ་ཁ་ཕྱེ་བ།",
"Toggle Fullscreen": "ཡོངས་ཁྱབ་བརྗེ་བ།",
"Close Window": "སྒེའུ་ཁུང་ཁ་རྒྱག",
"Quit App": "ཉེར་སྤྱོད་ཁ་རྒྱག",
"Go Left / Previous Page": "གཡོན་དུ / སྔོན་མའི་ཤོག་ངོས།",
"Go Right / Next Page": "གཡས་སུ / རྗེས་མའི་ཤོག་ངོས།",
"Go Up": "གོང་དུ།",
"Go Down": "འོག་ཏུ།",
"Previous Chapter": "སྔོན་མའི་ལེའུ།",
"Next Chapter": "རྗེས་མའི་ལེའུ།",
"Scroll Half Page Down": "ཤོག་ངོས་ཕྱེད་ཀ་འོག་ཏུ།",
"Scroll Half Page Up": "ཤོག་ངོས་ཕྱེད་ཀ་གོང་དུ།",
"Save Note": "ཟིན་བྲིས་ཉར་བ།",
"Single Section Scroll": "དོན་ཚན་གཅིག་འགུལ་བ།",
"General": "སྤྱིར་བཏང་།",
"Text to Speech": "ཡི་གེ་ནས་སྒྲ།",
"Zoom": "ཆེ་རུ་གཏོང་།",
"Window": "སྒེའུ་ཁུང་།",
"Your Bookshelf": "ཁྱོད་ཀྱི་དཔེ་མཛོད།",
"TTS": "ཡི་གེ་ནས་སྒྲ",
"Media Info": "སྨྱན་བྱད་ཆ་འཕྲིན",
"Update Frequency": "གསར་སྒྱུར་ཐེངས་གྲངས",
"Every Sentence": "ཚིག་གྲུབ་རེ་རེ",
"Every Paragraph": "དུམ་མཚམས་རེ་རེ",
"Every Chapter": "ལེའུ་རེ་རེ",
"TTS Media Info Update Frequency": "TTS སྨྱན་བྱད་ཆ་འཕྲིན་གསར་སྒྱུར་ཐེངས་གྲངས",
"Select reading speed": "ཀློག་པའི་མྱུར་ཚད་འདེམས།",
"Drag to seek": "འཚོལ་བར་འདྲུད།",
"Punctuation Delay": "ཚེག་ཤད་ཕྱིར་འགྱངས།",
"Please confirm that this OPDS connection will be proxied through Readest servers on the web app before continuing.": "མུ་མཐུད་མ་བྱས་གོང་ OPDS འབྲེལ་མཐུད་འདི་དྲ་རྒྱའི་ཉེར་སྤྱོད་ཐོག་ Readest ཞབས་ཞུ་བ་བརྒྱུད་ནས་བསྐུར་རྒྱུ་ཡིན་པ་གཏན་འཁེལ་བྱོས།",
"Custom Headers (optional)": "སྲོལ་སྒྲིག་མགོ་བརྗེ (གདམ་གའི)",
"Add one header per line using \"Header-Name: value\".": "\"Header-Name: value\" བེད་སྤྱོད་བྱས་ནས་ཕྲེང་རེ་རེའི་ནང་མགོ་བརྗེ་གཅིག་སྣོན།",
"I understand this OPDS connection will be proxied through Readest servers on the web app. If I do not trust Readest with these credentials or headers, I should use the native app instead.": "OPDS འབྲེལ་མཐུད་འདི་དྲ་རྒྱའི་ཉེར་སྤྱོད་ཐོག་ Readest ཞབས་ཞུ་བ་བརྒྱུད་ནས་བསྐུར་རྒྱུ་ཡིན་པ་ངས་ཧ་གོ། ང་ Readest ལ་ངེས་འཛིན་འམ་མགོ་བརྗེ་འདི་དག་ཡིད་ཆེས་མེད་ན། ང་ས་གནས་ཉེར་སྤྱོད་བེད་སྤྱོད་བྱ་དགོས།",
"Unable to connect to Hardcover. Please check your network connection.": "Hardcover ལ་འབྲེལ་མཐུད་བྱེད་ཐུབ་མ་སོང་། ཁྱེད་ཀྱི་དྲ་རྒྱའི་འབྲེལ་མཐུད་ལ་ཞིབ་བཤེར་གནང་།",
"Invalid Hardcover API token": "Hardcover API ཐོབ་ཐང་མ་ཆིག་སྒྲིལ་མིན།",
"Disconnected from Hardcover": "Hardcover ནས་འབྲེལ་མཐུད་ཆད་སོང་།",
"Connect your Hardcover account to sync reading progress and notes.": "ཀློག་འགྲོས་དང་མཆན་ཟླ་སྒྲིག་བྱེད་པར་ཁྱེད་ཀྱི Hardcover ཐོ་ཁུངས་འབྲེལ་མཐུད་བྱོས།",
"Get your API token from hardcover.app → Settings → API.": "hardcover.app → སྒྲིག་འགོད → API ནས་ཁྱེད་ཀྱི API ཐོབ་ཐང་ལེན།",
"API Token": "API ཐོབ་ཐང་།",
"Paste your Hardcover API token": "ཁྱེད་ཀྱི Hardcover API ཐོབ་ཐང་སྦྱར།",
"Hardcover Sync": "Hardcover ཟླ་སྒྲིག",
"Push Notes": "མཆན་གཏོང་བ",
"No annotations or excerpts to sync for this book.": "དེབ་འདིའི་ཆེད་ཟླ་སྒྲིག་བྱེད་པའི་མཆན་ཡང་ན་དྲངས་ཡིག་མེད།",
"Configure Hardcover in Settings first.": "སྔོན་ལ་སྒྲིག་འགོད་ནང་ Hardcover རིགས་སྒྲིག་བྱོས།",
"No new Hardcover note changes to sync.": "ཟླ་སྒྲིག་བྱེད་པའི་ Hardcover མཆན་གསར་པ་མེད།",
"Hardcover synced: {{inserted}} new, {{updated}} updated, {{skipped}} unchanged": "Hardcover ཟླ་སྒྲིག་བྱས་ཟིན: {{inserted}} གསར་པ, {{updated}} གསར་བཅོས, {{skipped}} མི་འགྱུར",
"Hardcover notes sync failed: {{error}}": "Hardcover མཆན་ཟླ་སྒྲིག་མི་ཚོགས: {{error}}",
"Reading progress synced to Hardcover": "ཀློག་འགྲོས་ Hardcover ལ་ཟླ་སྒྲིག་བྱས་ཟིན།",
"Hardcover progress sync failed: {{error}}": "Hardcover ཀློག་འགྲོས་ཟླ་སྒྲིག་མི་ཚོགས: {{error}}",
"ISBN": "ISBN",
"Keep existing source identifier": "ཡོད་བཞིན་པའི་འབྱུང་ཁུངས་ཐོབ་ཐང་གཞག",
"Toggle Toolbar": "ལག་ཆའི་ཚན་བར་བརྗེ་བ།",
"Split Hyphens": "སྦྲེལ་རྟགས་གཅད་པ",
"Apply Theme Colors to PDF": "PDF ལ་བརྗོད་དོན་ཚོས་གཞི་སྤྱོད་པ",
"Name": "མིང",
"Remove": "བསུབ།",
"Edit OPDS Catalog": "OPDS དཀར་ཆག་རྩོམ་སྒྲིག",
"Save Changes": "བསྒྱུར་བཅོས་ཉར་ཚགས།",
"Use Book Layout": "དཔེ་ཆའི་བཀོད་སྒྲིག་སྤྱོད་པ",
"End of this section. Continue to the next.": "སྡེ་ཚན་འདིའི་མཇུག་རེད། རྗེས་མའི་སྡེ་ཚན་དུ་མུ་མཐུད་རོགས།",
"Auto-download": "རང་འགུལ་ཕབ་ལེན།",
"Auto-download new items": "གསར་འདོན་གསར་པ་རང་འགུལ་ཕབ་ལེན།",
"Automatically download new publications when the app syncs": "མཉེན་ཆས་མཉམ་སྦྱོར་སྐབས་གསར་འདོན་གསར་པ་རང་འགུལ་ཕབ་ལེན་བྱེད།",
"{{count}} new item(s) downloaded from OPDS_other": "OPDS ནས་གསར་འདོན་གསར་པ་ {{count}} ཕབ་ལེན་བྱས།",
"Failed to sync {{count}} OPDS catalog(s)_other": "OPDS དཀར་ཆག་ {{count}} མཉམ་སྦྱོར་མ་ཐུབ།",
"Last synced {{when}}": "མཐའ་མའི་མཐུན་སྒྲིག་ {{when}}",
"Failed downloads": "ཕབ་ལེན་ཕམ་ཁ།",
"No failed downloads": "ཕབ་ལེན་ཕམ་ཁ་མེད།",
"Attempts: {{count}}_other": "ཐེངས་ {{count}}",
"Skip": "གཡོལ།",
"Skip all": "ཡོད་ཚད་གཡོལ།",
"Retry all": "ཡོད་ཚད་ཡང་སྐྱར།",
"{{count}} failed_other": "{{count}} ཕམ་ཁ།",
"(none)": "(མེད)",
"Identifiers": "ངོས་འཛིན་བྱེད་མཁན",
"Read (Stream)": "ཀློག་པ། (རྒྱུན་སྤེལ།)",
"Failed to start stream": "རྒྱུན་སྤེལ་འགོ་འཛུགས་མ་ཐུབ།",
"No dictionaries enabled": "ཚིག་མཛོད་གང་ཡང་སྒོ་མི་འབྱེད།",
"Enable a dictionary in Settings → Language → Dictionaries.": "སྒྲིག་འགོད ← སྐད་ཡིག ← ཚིག་མཛོད་ནས་ཚིག་མཛོད་ཤིག་ལ་སྒོ་འབྱེད།",
"Wiktionary": "ཝི་ཀེ་ཤོག་ནར།",
"Drag to reorder": "གོ་རིམ་སྒྱུར་བར་འཐེན།",
"Built-in": "ནང་ཁུལ།",
"Bundle is missing on this device. Re-import to use it.": "ཡོ་བྱད་འདི་དུས་ཆས་ཁྲོད་མི་འདུག འདི་སྤྱོད་པར་ཡང་བསྐྱར་ནང་འདྲེན་གནང་རོགས།",
"This dictionary format is not supported.": "ཚིག་མཛོད་འདིའི་རྣམ་པར་རྒྱབ་སྐྱོར་མི་བྱེད།",
"MDict": "MDict",
"StarDict": "StarDict",
"Imported {{count}} dictionary_other": "ཚིག་མཛོད་ {{count}} ནང་འདྲེན་གྲུབ།",
"Skipped incomplete bundles: {{names}}": "ཚང་མི་འདུག་པའི་ཡོ་བྱད་སྤོས་ནས་འགྲོ་སོང༌། {{names}}",
"Failed to import dictionary: {{message}}": "ཚིག་མཛོད་ནང་འདྲེན་གྲུབ་མ་སོང༌། {{message}}",
"Dictionaries": "ཚིག་མཛོད་ཁག",
"Delete Dictionary": "ཚིག་མཛོད་སུབ།",
"Importing…": "ནང་འདྲེན་བྱེད་བཞིན་པ…",
"Import Dictionary": "ཚིག་མཛོད་ནང་འདྲེན།",
"No dictionaries available.": "ཚིག་མཛོད་གང་ཡང་མི་འདུག།",
"StarDict bundles need .ifo, .idx, and .dict.dz files (.syn optional).": "StarDict ཡོ་བྱད་ལ་ .ifo དང་ .idx, .dict.dz ཡིག་ཆ་ཁག་དགོས་པ་ཡིན། (.syn ནི་འདེམས་འཁྱོངས་ཡིན།)",
"Select all the bundle files together when importing.": "ནང་འདྲེན་སྐབས་ཡོ་བྱད་ཡིག་ཆ་ཡོངས་མཉམ་དུ་འདེམས་རོགས།",
"Manage Dictionaries": "ཚིག་མཛོད་སྟངས་འཛིན།",
"Select Dictionary Files": "ཚིག་མཛོད་ཡིག་ཆ་འདེམས།",
"Read on Wikipedia →": "ཝི་ཁི་ཕིཌིཡ་ནས་ཀློག →",
"This link can't be opened": "འདྲེན་བྱང་འདི་ཁ་ཕྱེ་ཐུབ་མེད།",
"The annotation link is missing required information. The original link may have been truncated.": "དྲན་བྱང་འདྲེན་བྱང་ལ་དགོས་ངེས་གནས་ཚུལ་མི་འདུག འདྲེན་བྱང་གདོད་མ་གཅོད་ཡོད་སྲིད།",
"Go to Readest": "Readest ལ་འགྲོ",
"Open-source ebook reader for everyone, on every device.": "མི་ཀུན་ལ། སྒྲིག་ཆས་སྣ་ཚོགས་ཐོག་ཏུ་སྤྱོད་རུང་བའི་འབྱེད་པའི་ཀོ་ཌ་ཨི་དེབ་ཀློག་ཆས།",
"Open in Readest": "Readest ནང་ཁ་ཕྱེ",
"If Readest didn't open automatically, choose an option below:": "Readest རང་འགུལ་གྱིས་ཁ་ཕྱེ་མེད་ན། འོག་གི་གདམ་ག་ཞིག་འདེམས་རོགས།",
"Continue reading where you left off.": "ཁྱོད་ཀྱིས་མཚམས་བཞག་སའི་ས་ནས་མུ་མཐུད་ཀློག་རོགས།",
"Readest logo": "Readest ཀྱི་ལས་རྟགས།",
"Opening Readest...": "Readest ཁ་ཕྱེ་བཞིན་པ…",
"Open in Readest app": "Readest ཉེར་སྤྱོད་ནང་ཁ་ཕྱེ",
"Continue in browser": "བརྡར་ཞིབ་ཆས་ནང་མུ་མཐུད",
"Don't have Readest?": "Readest མེད་དམ།",
"Book not in your library": "དེབ་འདི་ཁྱེད་ཀྱི་དཔེ་མཛོད་ནང་མི་འདུག",
"Share Book": "དཔེ་ཆ་མཉམ་སྤྱོད།",
"Could not create share link": "མཉམ་སྤྱོད་འབྲེལ་མཐུད་བཟོ་མ་ཐུབ།",
"Link copied": "འབྲེལ་མཐུད་པར་སློག་གྲུབ།",
"Could not copy link": "འབྲེལ་མཐུད་པར་སློག་མ་ཐུབ།",
"Share revoked": "མཉམ་སྤྱོད་ཕྱིར་འཐེན།",
"Could not revoke share": "མཉམ་སྤྱོད་ཕྱིར་འཐེན་མ་ཐུབ།",
"Uploading book…": "དཔེ་ཆ་ཡར་སྐྱེལ་བཞིན་པ…",
"Generating…": "བཟོ་བཞིན་པ…",
"Generate share link": "མཉམ་སྤྱོད་འབྲེལ་མཐུད་བཟོ།",
"Share URL": "མཉམ་སྤྱོད་གནས་སྡོད།",
"Copy link": "འབྲེལ་མཐུད་པར་སློག",
"Copied": "པར་སློག་གྲུབ།",
"Share via…": "བརྒྱུད་ནས་མཉམ་སྤྱོད…",
"Expires {{date}}": "{{date}} ལ་དུས་ཟད།",
"Revoking…": "ཕྱིར་འཐེན་བཞིན་པ…",
"Revoke share": "མཉམ་སྤྱོད་ཕྱིར་འཐེན།",
"Sign in to share books": "དཔེ་ཆ་མཉམ་སྤྱོད་པར་ཐོ་འགོད་གནང་རོགས།",
"Expiring soon": "མྱུར་དུ་ཟད་ངེས།",
"Missing share token": "མཉམ་སྤྱོད་ཐམ་ག་མེད།",
"Could not add to your library": "ཁྱེད་ཀྱི་དཔེ་མཛོད་ལ་སྣོན་མ་ཐུབ།",
"This share link is no longer available": "མཉམ་སྤྱོད་འབྲེལ་མཐུད་འདི་མི་འཐོབ།",
"The original link may have expired or been revoked.": "གདོད་མའི་འབྲེལ་མཐུད་དུས་ཟད་པའམ་ཕྱིར་འཐེན་སྲིད།",
"Get Readest": "Readest བཞེས་པ།",
"Loading shared book…": "མཉམ་སྤྱོད་དཔེ་ཆ་མར་སྐྱེལ་བཞིན་པ…",
"Adding…": "སྣོན་བཞིན་པ…",
"Add to my library": "ངའི་དཔེ་མཛོད་ལ་སྣོན།",
"Could not load your shares": "ཁྱེད་ཀྱི་མཉམ་སྤྱོད་མར་སྐྱེལ་མ་ཐུབ།",
"Revoked": "ཕྱིར་འཐེན།",
"Expired": "དུས་ཟད།",
"Shared books": "མཉམ་སྤྱོད་དཔེ་ཆ།",
"You haven't shared any books yet": "ཁྱོས་ད་དུང་དཔེ་ཆ་གང་ཡང་མཉམ་སྤྱོད་མ་བྱས།",
"Open a book and tap Share to send it to a friend.": "དཔེ་ཆ་ཞིག་ཁ་ཕྱེ་ནས་གྲོགས་པོ་ལ་སྐུར་ཆེད་མཉམ་སྤྱོད་གཏུགས།",
"{{count}} active_other": "{{count}} ལས་འཛིན་བཞིན་པ།",
"{{count}} downloads_other": "{{count}} ཕབ་ལེན།",
"starts at saved page": "ཉར་ཚགས་ཤོག་ངོས་ནས་འགོ་འཛུགས།",
"More actions": "བྱ་འགུལ་མང་བ།",
"Loading…": "མར་སྐྱེལ་བཞིན་པ…",
"Load more": "མང་བ་མར་སྐྱེལ།",
"Could not load shared book": "མཉམ་སྤྱོད་དཔེ་ཆ་མར་སྐྱེལ་མ་ཐུབ།",
"The share link is missing required information.": "མཉམ་སྤྱོད་འབྲེལ་མཐུད་ལ་ཆ་རྐྱེན་མེད།",
"Please check your connection and try again.": "ཁྱེད་ཀྱི་འབྲེལ་མཐུད་ཞིབ་འཇུག་གནང་ནས་ཡང་བསྐྱར་ཐེངས་གཅིག་འབད་རོགས།",
"Sign in to import shared books": "མཉམ་སྤྱོད་དཔེ་ཆ་འདྲེན་པར་ཐོ་འགོད་གནང་རོགས།",
"Already in your library": "ཁྱེད་ཀྱི་དཔེ་མཛོད་ནང་ཡོད་ཟིན་པ།",
"Added to your library": "ཁྱེད་ཀྱི་དཔེ་མཛོད་ལ་སྣོན་ཟིན།",
"Could not import shared book": "མཉམ་སྤྱོད་དཔེ་ཆ་འདྲེན་མ་ཐུབ།",
"Manage Shared Links": "མཉམ་སྤྱོད་འབྲེལ་མཐུད་དོ་དམ།",
"Expires in": "དུས་ཟད།",
"{{count}} days_other": "ཉིན་{{count}}",
"Expires in {{count}} days_other": "ཉིན་{{count}}ནང་ཟད།",
"Expires in {{count}} hours_other": "ཆུ་ཚོད་{{count}}ནང་ཟད།",
"Open in app": "ཉེར་སྤྱོད་ནང་ཁ་ཕྱེ།",
"Shared with you": "ཁྱེད་དང་མཉམ་སྤྱོད་བྱས།",
"Downloading… {{percent}}%": "ཕབ་ལེན་བྱེད་བཞིན་པ… {{percent}}%",
"Import progress": "ནང་འདྲེན་གྱི་ཡར་ཐོན།",
"Share reading progress": "ཀློག་འདོན་གྱི་ཡར་ཐོན་སྤེལ་མཁོ།",
"Includes your reading progress": "ཁྱེད་ཀྱི་ཀློག་འདོན་གྱི་ཡར་ཐོན་འདུས།",
"Synced {{time}}": "{{time}}མཉམ་སྒྲིག་བྱས་པ",
"Sync Info": "མཉམ་སྒྲིག་གྱི་ཆ་འཕྲིན",
"Last Synced": "མཐའ་མའི་མཉམ་སྒྲིག",
"DICT": "DICT",
"Slob": "Slob",
"DICT bundles need a .index file and a .dict.dz file.": "DICT ཕུང་པོར་ .index ཡིག་ཆ་དང་ .dict.dz ཡིག་ཆ་གཉིས་ཀ་དགོས།",
"Slob bundles need a .slob file.": "Slob ཕུང་པོར་ .slob ཡིག་ཆ་ཞིག་དགོས།",
"URL template must start with http(s):// and contain %WORD%.": "URL ཕྱི་མོ་ http(s):// གིས་འགོ་འཛུགས་དགོས་ལ་ %WORD% ཡོད་དགོས།",
"Web": "དྲ་རྒྱ",
"Add Web Search": "དྲ་རྒྱའི་འཚོལ་བཤེར་སྣོན།",
"Edit Web Search": "དྲ་རྒྱའི་འཚོལ་བཤེར་རྩོམ་སྒྲིག",
"e.g. Google": "དཔེར་ན། Google",
"URL Template": "URL ཕྱི་མོ",
"Use %WORD% where the looked-up word should appear.": "འཚོལ་བཤེར་བྱས་པའི་ཚིག་འཆར་ས་གནས་སུ་ %WORD% བཀོལ་སྤྱོད་གནང་རོགས།",
"Open the search result in your browser:": "ཁྱེད་ཀྱི་བརྡ་འཚོལ་མཁོ་ཆས་ལ་འཚོལ་བཤེར་གྲུབ་འབྲས་ཁ་ཕྱེ:",
"Open in {{name}}": "{{name}} ནང་ཁ་ཕྱེ།",
"Book Fingerprint": "དཔེ་ཆའི་མཛུབ་རྗེས།",
"Search the web": "དྲ་ངོས་སུ་འཚོལ་ཞིབ།",
"App deeplink (readest://)": "ཉེར་སྤྱོད་ཟབ་སྦྲེལ (readest://)",
"Universal web link (https://)": "སྤྱི་ཁྱབ་དྲ་སྦྲེལ (https://)",
"Name cannot be empty.": "མིང་སྟོང་པར་འཇོག་མི་རུང་།",
"Replaced {{count}} existing dictionary_other": "གཞི་ནས་ཡོད་པའི་ཚིག་མཛོད་ {{count}} བརྗེ་སྒྱུར་བྱས།",
"Cancel Edit": "བཟོ་བཅོས་ཕྱིར་འཐེན།",
"Edit Dictionary": "ཚིག་མཛོད་བཟོ་བཅོས།",
"MDict bundles use .mdx files; companion .mdd and .css files are optional.": "MDict གི་ཡིག་ཆས་ཚོགས་ཀྱིས་ .mdx ཡིག་ཆ་སྤྱོད། སྦྲེལ་མཐུན་ .mdd དང་ .css ཡིག་ཆ་ཡིན་ན་འདེམས་ཆོག",
"Dictionary name": "ཚིག་མཛོད་མིང་།",
"This audio can't play here — the dictionary uses an outdated format. Try one with Opus, MP3, or WAV audio.": "སྒྲ་འདི་འདིར་སྒྲོག་མི་ཐུབ — ཚིག་མཛོད་འདིས་རྩིས་མེད་པའི་རྣམ་པ་སྤྱོད། Opus, MP3, ཡང་ན་ WAV གི་སྒྲ་ལྡན་པ་ཞིག་ལ་འདེམས་སྡུར་གྱིས།",
"File uploaded: {{title}}": "ཡིག་ཆ་སྤྲད་ཟིན། {{title}}",
"File downloaded: {{title}}": "ཡིག་ཆ་ཕབ་ལེན་བྱས་ཟིན། {{title}}",
"Deleted cloud copy of the file: {{title}}": "ཡིག་ཆའི་སྤྲི་དོན་གྱི་འདྲ་བཤུས་བསུབ་ཟིན། {{title}}",
"Failed to upload file: {{title}}": "ཡིག་ཆ་སྤྲད་མ་ཐུབ། {{title}}",
"Failed to download file: {{title}}": "ཡིག་ཆ་ཕབ་ལེན་བྱས་མ་ཐུབ། {{title}}",
"Failed to delete cloud copy of the file: {{title}}": "ཡིག་ཆའི་སྤྲི་དོན་གྱི་འདྲ་བཤུས་བསུབ་ཐུབ་མེད། {{title}}",
"{{percentage}}% used": "སྤྱོད་ཟིན་ {{percentage}}%",
"Set PIN…": "PIN སྒྲིག་པ་…",
"Require a 4-digit PIN to open Readest": "Readest ཁ་ཕྱེ་བར་ཨང་གྲངས་ ༤ ཡོད་པའི་ PIN ཞིག་དགོས",
"Change PIN…": "PIN བརྗེ་བ་…",
"Disable PIN…": "PIN སྤོ་བ་…",
"Sync passphrase ready": "མཉམ་སྦྱོར་གསང་ཚིག་གྲ་སྒྲིག་ཟིན་པ།",
"This permanently deletes the encrypted credentials we sync (e.g., OPDS catalog passwords) on every device. Local copies are preserved. You will need to re-enter the sync passphrase or set a new one. Continue?": "འདིས་ཡིག་ཆ་ཁག་གསང་སྦས་སུ་མཉམ་སྦྱོར་བྱས་པ་རྣམས་ (དཔེར་ན། OPDS དཀར་ཆག་གི་གསང་ཨང་) སྒྲིག་ཆས་རེ་རེའི་ཐོག་གཏན་འཇགས་སུ་སུབ་སྲིད། ས་གནས་ཀྱི་པར་གཞི་ཉར་ཡོད། ཁྱོད་ཀྱིས་མཉམ་སྦྱོར་གསང་ཚིག་སྔར་བཞིན་འཇུག་པའམ་གསར་པ་སྒྲིག་དགོས། མུ་མཐུད་དམ།",
"Sync passphrase forgotten — all encrypted fields cleared": "མཉམ་སྦྱོར་གསང་ཚིག་བརྗེད་སོང་ — གསང་སྦས་ཀྱི་ས་སྟོང་ཚང་མ་སུབ་ཟིན།",
"Sync passphrase": "མཉམ་སྦྱོར་གསང་ཚིག",
"Set passphrase": "གསང་ཚིག་སྒྲིག་པ།",
"Forgot passphrase": "གསང་ཚིག་བརྗེད་སོང་།",
"Incorrect PIN": "PIN ནོར་འདུག",
"App locked": "མཉེན་ཆས་སྒོ་བརྒྱབ་ཡོད",
"Enter your PIN": "ཁྱེད་ཀྱི་ PIN འབྲི་རོགས",
"Readest is locked. Enter your 4-digit PIN to continue.": "Readest སྒོ་བརྒྱབ་ཡོད། མུ་མཐུད་པར་ཨང་གྲངས་ ༤ ཡོད་པའི་ PIN འབྲི་རོགས།",
"PIN code": "PIN ཨང་གྲངས",
"Forgetting your PIN locks you out of this device. You'll need to clear the app's data to reset it.": "PIN བརྗེད་ན་ཁྱེད་ཀྱིས་སྒྲིག་ཆས་འདིར་འཛུལ་མི་ཐུབ། སླར་སྒྲིག་པར་མཉེན་ཆས་ཀྱི་གཞི་གྲངས་གཙང་སེལ་བྱ་དགོས།",
"Passphrase must be at least 8 characters": "གསང་ཚིག་ལ་ཡི་གེ་གྲངས་ ༨ ལས་མི་ཉུང་བ་དགོས།",
"Passphrases do not match": "གསང་ཚིག་གཉིས་མཐུན་མི་འདུག",
"Set sync passphrase": "མཉམ་སྦྱོར་གསང་ཚིག་སྒྲིག་པ།",
"Enter sync passphrase": "མཉམ་སྦྱོར་གསང་ཚིག་འཇུག་རོགས།",
"A sync passphrase encrypts your sensitive fields (like OPDS catalog credentials) before they sync. We never see this passphrase. Pick something memorable — there is no recovery without it.": "མཉམ་སྦྱོར་གསང་ཚིག་གིས་གལ་ཆེའི་ས་སྟོང་རྣམས་ (དཔེར་ན་ OPDS དཀར་ཆག་གི་ངོས་འཛིན་ཆ་འཕྲིན) མཉམ་སྦྱོར་མ་བྱས་གོང་གསང་སྦས་སུ་སྒྱུར། ང་ཚོས་གསང་ཚིག་འདི་ནམ་ཡང་མཐོང་མི་ཐུབ། དྲན་སླ་བ་ཞིག་འདེམས་རོགས། — དེ་མེད་ན་སླར་འཚོལ་མི་ཐུབ།",
"Enter the sync passphrase you set on another device to decrypt your synced credentials.": "ཁྱེད་ཀྱི་མཉམ་སྦྱོར་ངོས་འཛིན་ཆ་འཕྲིན་གསང་སྦས་འགྲོལ་ཆེད་སྒྲིག་ཆས་གཞན་པོར་སྒྲིག་པའི་མཉམ་སྦྱོར་གསང་ཚིག་འཇུག་རོགས།",
"Confirm passphrase": "གསང་ཚིག་གཏན་འཁེལ།",
"Unlock": "ཁ་ཕྱེ།",
"Set PIN": "PIN སྒྲིག་པ",
"Change PIN": "PIN བརྗེ་བ",
"Disable PIN": "PIN སྤོ་བ",
"Enter your current PIN, then choose a new 4-digit PIN.": "ཁྱེད་ཀྱི་ད་ལྟའི་ PIN འབྲི་རྗེས། ཨང་གྲངས་ ༤ ཡོད་པའི་ PIN གསར་པ་ཞིག་འདེམས།",
"Enter your current PIN to disable the app lock.": "མཉེན་ཆས་ཀྱི་སྒོ་བརྒྱབ་པ་སྤོ་བར་ཁྱེད་ཀྱི་ད་ལྟའི་ PIN འབྲི་རོགས།",
"PIN must be {{length}} digits": "PIN ནི་ཨང་གྲངས་ {{length}} ཡིན་དགོས",
"PINs do not match": "PIN མི་འདྲ",
"Current PIN": "ད་ལྟའི་ PIN",
"New PIN": "PIN གསར་པ",
"Confirm new PIN": "PIN གསར་པ་གཏན་འཁེལ",
"Manage Sync": "མཉམ་འགྲོ་སྟངས་འཛིན།",
"Imported book files and library metadata": "ནང་འཇུག་བྱས་པའི་དཔེ་ཆའི་ཡིག་ཆ་དང་དཔེ་མཛོད་མཛོད་གནད་སྡུད།",
"Last-read position, bookmarks, and per-book preferences": "མཐའ་མཇུག་ཀློག་སའི་གནས་ས། དཔེ་རྟགས། དཔེ་ཆ་རེ་རེའི་འདེམས་སྒྲིག།",
"Highlights and notes": "གསལ་རྟགས་དང་ཟིན་བྲིས།",
"Fonts": "ཡིག་གཟུགས།",
"Custom font files": "རང་འདེམས་ཡིག་གཟུགས་ཀྱི་ཡིག་ཆ།",
"Backgrounds": "རྒྱབ་ལྗོངས།",
"Custom background textures": "རང་འདེམས་རྒྱབ་ལྗོངས་ཀྱི་རི་མོ།",
"OPDS catalogs": "OPDS དཀར་ཆག།",
"Saved catalog URLs and (encrypted) credentials": "ཉར་ཚགས་བྱས་པའི་དཀར་ཆག་གི་URL་དང་(གསང་འཇུག་བྱས་པའི་)ཐོབ་ཐང་ལག་འཁྱེར།",
"Wrong sync passphrase — synced credentials could not be decrypted": "མཉམ་འགྲོ་གསང་གྲངས་ནོར་འདུག — མཉམ་འགྲོ་བྱས་པའི་ཐོབ་ཐང་ལག་འཁྱེར་ཁ་ཕྱེ་མི་ཐུབ།",
"Sync passphrase data on the server was reset. Re-encrypting your credentials under the new passphrase…": "ཞབས་ཞུ་བའི་ཐོག་གི་མཉམ་འགྲོ་གསང་གྲངས་ཀྱི་གནད་སྡུད་སླར་སྒྲིག་བྱས་སོང་། གསང་གྲངས་གསར་པས་ཁྱེད་ཀྱི་ཐོབ་ཐང་ལག་འཁྱེར་སླར་གསང་འཇུག་བྱེད་བཞིན་པ་ཡིན…",
"Failed to decrypt synced credentials": "མཉམ་འགྲོ་བྱས་པའི་ཐོབ་ཐང་ལག་འཁྱེར་ཁ་ཕྱེ་བར་ཕམ་ཉེས་བྱུང་།",
"Imported dictionary bundles and settings": "ནང་འཇུག་བྱས་པའི་ཚིག་མཛོད་ཐུམ་སྒྲིལ་དང་སྒྲིག་འགོད།",
"Choose what syncs across your devices. Disabling a category stops this device from sending or receiving rows of that kind. Anything already on the server is left alone, re-enabling resumes from where you stopped.": "ཁྱེད་ཀྱི་སྒྲིག་ཆས་ཁག་གི་བར་ལ་གང་མཉམ་འགྲོ་དགོས་པ་འདེམས་རོགས། སྡེ་ཚན་ཞིག་ནུས་མེད་བཟོས་ན། སྒྲིག་ཆས་འདིས་རིགས་དེའི་ཡིག་ཆ་སྐུར་སྤྲོད་དམ་ལེན་པ་མཚམས་འཇོག་བྱེད། ཞབས་ཞུ་བ་སྟེང་གནས་ཟིན་པའི་གང་ཡང་འགྱུར་ལྡོག་མི་ཐེབས། ཡང་བསྐྱར་ནུས་ཅན་བཟོས་ཚེ། མཚམས་འཇོག་སའི་གནས་ནས་མུ་མཐུད་འགྲོ་བ་ཡིན།",
"Data Sync": "གཞི་གྲངས་མཉམ་འགྲོ།",
"Manage Fonts": "ཡིག་གཟུགས་སྟངས་འཛིན།",
"App settings": "མཉེན་ཆས་སྒྲིག་འགོད།",
"Theme, highlight colours, integrations (KOSync, Readwise, Hardcover), and dictionary order": "བརྗོད་གཞི། གསལ་རྟགས་ཁ་དོག ཟུང་འབྲེལ་ (KOSync, Readwise, Hardcover) དང་ཚིག་མཛོད་ཀྱི་གོ་རིམ།",
"Required while Dictionaries sync is enabled": "ཚིག་མཛོད་ཁག་མཉམ་འགྲོ་སྤྱོད་སྐབས་དགོས་མཁོ་ཡོད།",
"Resets in {{hours}} hr {{minutes}} min": "{{hours}} ཆུ་ཚོད་ {{minutes}} སྐར་མ་ནང་སླར་གསོ་བྱེད།",
"Pick a 4-digit PIN. You will need to enter it every time you open Readest. There is no way to recover a forgotten PIN. Clearing the app data is the only way to reset it.": "ཨང་གྲངས་བཞི་ཅན་གྱི་ PIN ཞིག་འདེམས་རོགས། Readest ཕྱེ་རུས་ཐེངས་རེར་ཁྱེད་ཀྱིས་དེ་ནང་འཇུག་དགོས། བརྗེད་སོང་བའི་ PIN ཞིག་སླར་གསོ་བྱེད་ཐབས་མེད། ཉེར་སྤྱོད་ཀྱི་གཞི་གྲངས་གཙང་སེལ་བྱེད་པ་ནི་སླར་སྒྲིག་བྱེད་ཐབས་གཅིག་པོ་ཡིན།",
"Credentials": "ངོས་སྦྱོར་ཆ་འཕྲིན།",
"Tokens, usernames, and passwords for OPDS, KOReader, Hardcover, and Readwise. When disabled, credentials remain on this device only and are never uploaded.": "OPDS, KOReader, Hardcover, དང་ Readwise བཅས་ཀྱི་ཐོ་ཀེན་དང་སྤྱོད་མི་མིང་དང་གསང་གྲངས། སྤོ་འཛུགས་མེད་པའི་སྐབས། ངོས་སྦྱོར་ཆ་འཕྲིན་ཡིག་ཆས་འདི་ཁོ་ནར་གནས་པ་དང་ནམ་ཡང་སྤོ་གཏོང་མི་བྱེད།",
"Sensitive synced fields are encrypted before upload. Set a passphrase now or later when encryption is needed.": "གཙོ་གནད་ཆགས་པའི་མཉམ་སྡེབ་ཐོ་ཁོངས་ཡིག་ཆ་སྤོ་གཏོང་མ་བྱས་སྔོན་ལ་གསང་སྦས་སུ་འགྱུར་བར་བཟོ། གསང་སྦས་དགོས་སྐབས་ད་ལྟ་འམ་ཕྱིས་སུ་གསང་ཚིག་ཕྲེང་གཅིག་སྒྲིག་འཛུགས་གནང་རོགས།",
"Saved to this account. You will be prompted for it when decrypting credentials.": "རྩིས་ཐོ་འདིར་ཉར་ཚགས་བྱས་ཡོད། ངོས་སྦྱོར་ཆ་འཕྲིན་ལ་གསང་སྦས་འགྲོལ་སྐབས་ཁྱེད་ལ་འདྲི་སློང་གནང་འགྱུར།",
"Reading progress on this device differs from \"{{deviceName}}\".": "སྒྲིག་ཆས་འདིའི་ཀློག་འགྲོས་ནི་\"{{deviceName}}\"དང་མི་འདྲ།",
"This device": "སྒྲིག་ཆས་འདི།",
"Red": "དམར་པོ།",
"Yellow": "སེར་པོ།",
"Green": "ལྗང་ཁུ།",
"Blue": "སྔོན་པོ།",
"Violet": "མུ་མེན།",
"Edit color": "ཁ་དོག་རྩོམ་སྒྲིག",
"Add custom color": "རང་འདེམས་ཁ་དོག་སྣོན་པ།",
"Add label": "བྱང་བུ་སྣོན་པ།",
"Choose color": "ཁ་དོག་འདེམས་པ།",
"Connected to Hardcover. Last synced {{time}}.": "Hardcover ལ་མཐུད་ཟིན། མཐའ་མའི་མཉམ་སྒྲིག་ {{time}}།",
"Integrations": "མཉམ་སྦྲེལ།",
"Hardcover": "Hardcover",
"Connected to Readwise. Last synced {{time}}.": "Readwise ལ་མཐུད་ཟིན། མཐའ་མའི་མཉམ་སྒྲིག་ {{time}}།",
"Readwise": "Readwise",
"Browse and download books from online catalogs.": "དྲ་ཐོག་དཀར་ཆག་ནས་དེབ་བལྟ་ཞིབ་དང་ཕབ་ལེན་བྱེད་པ།",
"Connected as {{user}}": "{{user}} ཡི་མིང་འོག་མཐུད་ཟིན།",
"Not connected": "མ་མཐུད།",
"{{count}} catalog_other": "དཀར་ཆག་ {{count}}",
"No catalogs": "དཀར་ཆག་མེད།",
"Connect Readest to external services for sync, highlights, and catalogs.": "Readest ཕྱི་ཕྱོགས་ཞབས་ཞུ་ལ་སྦྲེལ་ནས་མཉམ་སྒྲིག་དང་གསལ་སྟོན། དཀར་ཆག་སོགས་སྤྱོད།",
"Reading Sync": "ཀློག་པའི་མཉམ་སྒྲིག",
"Content Sources": "ནང་དོན་འབྱུང་ཁུངས།",
"Scroll tabs": "ཤོག་བྱང་འགུལ་སྐྱོད།",
"Catalog actions": "དཔེ་ཐོའི་བྱ་སྤྱོད།",
"File Content": "ཡིག་ཆའི་ནང་དོན།",
"Start your library": "ཁྱེད་ཀྱི་དཔེ་མཛོད་འགོ་འཛུགས།",
"Pick a book from your device to add it to your library.": "ཁྱེད་ཀྱི་སྒྲིག་ཆས་ནས་དཔེ་དེབ་ཅིག་འདེམས་ནས་ཁྱེད་ཀྱི་དཔེ་མཛོད་ལ་ཁ་སྣོན་གྱིས།",
"Drop a book anywhere on this window, or pick one from your computer.": "སྒེའུ་ཁུང་འདིའི་གང་སར་དཔེ་དེབ་ཅིག་འཇོག་པའམ་ཡང་ན་ཁྱེད་ཀྱི་གློག་ཀླད་ནས་འདེམས་རོགས།",
"Sign in to sync your library": "ཁྱེད་ཀྱི་དཔེ་མཛོད་མཉམ་སྦྱོར་གྱི་ཆེད་ནང་བསྐྱོད་གནང་རོགས།"
}
@@ -101,11 +101,9 @@
"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.",
"Wikipedia": "Wikipedia",
"Writing Mode": "Schreibmodus",
"Your Library": "Ihre Bibliothek",
"TTS not supported for PDF": "TTS wird für PDF nicht unterstützt",
"Override Book Font": "Buch-Schriftart überschreiben",
"Apply to All Books": "Auf alle Bücher anwenden",
"Apply to This Book": "Auf dieses Buch anwenden",
@@ -172,9 +170,6 @@
"Token": "Token",
"Your OTP token": "Ihr OTP-Token",
"Verify token": "Token überprüfen",
"Sign in with Google": "Mit Google anmelden",
"Sign in with Apple": "Mit Apple anmelden",
"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.",
"Community Support": "Community-Support",
@@ -187,7 +182,6 @@
"RTL Direction": "RTL-Richtung",
"Maximum Column Height": "Maximale Spaltenhöhe",
"Maximum Column Width": "Maximale Spaltenbreite",
"Continuous Scroll": "Kontinuierliches Scrollen",
"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",
@@ -303,7 +297,6 @@
"No notes match your search": "Noch keine Notizen gefunden",
"Search notes and excerpts...": "Notizen und Auszüge suchen...",
"Sign in to Sync": "Anmelden zum Synchronisieren",
"Synced at {{time}}": "Synchronisiert um {{time}}",
"Never synced": "Nie synchronisiert",
"Show Remaining Time": "Verbleibende Zeit anzeigen",
"{{time}} min left in chapter": "{{time}} min verbleibend im Kapitel",
@@ -337,11 +330,11 @@
"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",
"{{count}} pages left in chapter_one": "<0>{{count}}</0><1> Seite verbleibend im Kapitel</1>",
"{{count}} pages left in chapter_other": "<0>{{count}}</0><1> Seiten verbleibend im Kapitel</1>",
"Show Remaining Pages": "Verbleibende Seiten anzeigen",
"Source Han Serif CN VF": "Source Han Serif",
"Huiwen-mincho": "Huiwen Mincho",
"Source Han Serif CN": "Source Han Serif",
"Huiwen-MinchoGBK": "Huiwen Mincho",
"KingHwa_OldSong": "KingHwa Song",
"Manage Subscription": "Abo verwalten",
"Coming Soon": "Demnächst verfügbar",
@@ -466,14 +459,12 @@
"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",
"Checksum Method": "Prüfziffernverfahren",
"File Content (recommended)": "Dateiinhalte (empfohlen)",
"File Name": "Dateiname",
"Device Name": "Gerätename",
"Connect to your KOReader Sync server.": "Mit Ihrem KOReader Sync-Server verbinden.",
@@ -484,10 +475,7 @@
"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}}%)",
@@ -602,11 +590,10 @@
"Size": "Größe",
"Cover": "Cover",
"Contain": "Inhalt",
"{{number}} pages left in chapter": "{{number}} Seiten verbleibend im Kapitel",
"{{number}} pages left in chapter": "<0>{{number}}</0><1> Seiten verbleibend im Kapitel</1>",
"Device": "Gerät",
"E-Ink Mode": "E-Ink-Modus",
"Highlight Colors": "Hervorhebungsfarben",
"Auto Screen Brightness": "Automatische Bildschirmhelligkeit",
"Pagination": "Seitenumbruch",
"Disable Double Tap": "Doppeltippen deaktivieren",
"Tap to Paginate": "Tippen zum Blättern",
@@ -641,7 +628,6 @@
"Strikethrough": "Durchgestrichen",
"Squiggly": "Wellig",
"Outline": "Umriss",
"Save Current Color": "Aktuelle Farbe speichern",
"Quick Colors": "Schnellfarben",
"Highlighter": "Textmarker",
"Save Book Cover": "Buchcover speichern",
@@ -699,9 +685,7 @@
"Validating...": "Wird überprüft...",
"View All": "Alle anzeigen",
"Forward": "Weiter",
"OPDS Catalog": "OPDS-Katalog",
"Home": "Start",
"Library": "Bibliothek",
"{{count}} items_one": "{{count}} Element",
"{{count}} items_other": "{{count}} Elemente",
"Download completed": "Download abgeschlossen",
@@ -721,5 +705,712 @@
"Last": "Letzte",
"Cannot Load Page": "Seite kann nicht geladen werden",
"An error occurred": "Ein Fehler ist aufgetreten",
"Online Library": "Online-Bibliothek"
"Online Library": "Online-Bibliothek",
"URL must start with http:// or https://": "Die URL muss mit http:// oder https:// beginnen",
"Title, Author, Tag, etc...": "Titel, Autor, Tag, etc...",
"Query": "Suchbegriff",
"Subject": "Thema",
"Enter {{terms}}": "Gib {{terms}} ein",
"No search results found": "Keine Suchergebnisse gefunden",
"Failed to load OPDS feed: {{status}} {{statusText}}": "OPDS-Feed konnte nicht geladen werden: {{status}} {{statusText}}",
"Search in {{title}}": "Suche in {{title}}",
"Manage Storage": "Speicher verwalten",
"Failed to load files": "Dateien konnten nicht geladen werden",
"Deleted {{count}} file(s)_one": "{{count}} Datei gelöscht",
"Deleted {{count}} file(s)_other": "{{count}} Dateien gelöscht",
"Failed to delete {{count}} file(s)_one": "Löschen von {{count}} Datei fehlgeschlagen",
"Failed to delete {{count}} file(s)_other": "Löschen von {{count}} Dateien fehlgeschlagen",
"Failed to delete files": "Dateien konnten nicht gelöscht werden",
"Total Files": "Gesamtdateien",
"Total Size": "Gesamtgröße",
"Quota": "Kontingent",
"Used": "Verwendet",
"Files": "Dateien",
"Search files...": "Dateien suchen...",
"Newest First": "Neueste zuerst",
"Oldest First": "Älteste zuerst",
"Largest First": "Größte zuerst",
"Smallest First": "Kleinste zuerst",
"Name A-Z": "Name AZ",
"Name Z-A": "Name ZA",
"{{count}} selected_one": "{{count}} ausgewählt",
"{{count}} selected_other": "{{count}} ausgewählt",
"Delete Selected": "Ausgewählte löschen",
"Created": "Erstellt",
"No files found": "Keine Dateien gefunden",
"No files uploaded yet": "Noch keine Dateien hochgeladen",
"files": "Dateien",
"Page {{current}} of {{total}}": "Seite {{current}} von {{total}}",
"Are you sure to delete {{count}} selected file(s)?_one": "Möchten Sie {{count}} ausgewählte Datei wirklich löschen?",
"Are you sure to delete {{count}} selected file(s)?_other": "Möchten Sie {{count}} ausgewählte Dateien wirklich löschen?",
"Cloud Storage Usage": "Cloud-Speichernutzung",
"Rename Group": "Gruppe umbenennen",
"From Directory": "Aus Verzeichnis",
"Successfully imported {{count}} book(s)_one": "Erfolgreich 1 Buch importiert",
"Successfully imported {{count}} book(s)_other": "Erfolgreich {{count}} Bücher importiert",
"Count": "Anzahl",
"Start Page": "Startseite",
"Search in OPDS Catalog...": "Im OPDS-Katalog suchen...",
"Please log in to use advanced TTS features": "Bitte melde dich an, um erweiterte TTS-Funktionen zu nutzen",
"Word limit of 30 words exceeded.": "Das Wortlimit von 30 Wörtern wurde überschritten.",
"Proofread": "Korrekturlesen",
"Current selection": "Aktuelle Auswahl",
"All occurrences in this book": "Alle Vorkommen in diesem Buch",
"All occurrences in your library": "Alle Vorkommen in Ihrer Bibliothek",
"Selected text:": "Ausgewählter Text:",
"Replace with:": "Ersetzen durch:",
"Enter text...": "Text eingeben …",
"Case sensitive:": "Groß-/Kleinschreibung:",
"Scope:": "Geltungsbereich:",
"Selection": "Auswahl",
"Library": "Bibliothek",
"Yes": "Ja",
"No": "Nein",
"Proofread Replacement Rules": "Korrektur-Ersetzungsregeln",
"Selected Text Rules": "Regeln für ausgewählten Text",
"No selected text replacement rules": "Keine Ersetzungsregeln für ausgewählten Text",
"Book Specific Rules": "Buchspezifische Regeln",
"No book-level replacement rules": "Keine Ersetzungsregeln auf Buchebene",
"Disable Quick Action": "Schnellaktion deaktivieren",
"Enable Quick Action on Selection": "Schnellaktion bei Auswahl aktivieren",
"None": "Keine",
"Annotation Tools": "Anmerkungswerkzeuge",
"Enable Quick Actions": "Schnellaktionen aktivieren",
"Quick Action": "Schnellaktion",
"Copy to Notebook": "In Notizbuch kopieren",
"Copy text after selection": "Text nach Auswahl kopieren",
"Highlight text after selection": "Text nach Auswahl hervorheben",
"Annotate text after selection": "Text nach Auswahl annotieren",
"Search text after selection": "Text nach Auswahl suchen",
"Look up text in dictionary after selection": "Text nach Auswahl im Wörterbuch nachschlagen",
"Translate text after selection": "Text nach Auswahl übersetzen",
"Read text aloud after selection": "Text nach Auswahl vorlesen",
"Proofread text after selection": "Text nach Auswahl Korrektur lesen",
"{{activeCount}} active, {{pendingCount}} pending": "{{activeCount}} aktiv, {{pendingCount}} ausstehend",
"{{failedCount}} failed": "{{failedCount}} fehlgeschlagen",
"Waiting...": "Warten...",
"Failed": "Fehlgeschlagen",
"Completed": "Abgeschlossen",
"Cancelled": "Abgebrochen",
"Retry": "Erneut versuchen",
"Active": "Aktiv",
"Transfer Queue": "Übertragungswarteschlange",
"Upload All": "Alle hochladen",
"Download All": "Alle herunterladen",
"Resume Transfers": "Übertragungen fortsetzen",
"Pause Transfers": "Übertragungen pausieren",
"Pending": "Ausstehend",
"No transfers": "Keine Übertragungen",
"Retry All": "Alle erneut versuchen",
"Clear Completed": "Abgeschlossene löschen",
"Clear Failed": "Fehlgeschlagene löschen",
"Upload queued: {{title}}": "Upload in Warteschlange: {{title}}",
"Download queued: {{title}}": "Download in Warteschlange: {{title}}",
"Book not found in library": "Buch nicht in der Bibliothek gefunden",
"Unknown error": "Unbekannter Fehler",
"Please log in to continue": "Bitte melden Sie sich an, um fortzufahren",
"Cloud File Transfers": "Cloud-Dateiübertragungen",
"Show Search Results": "Suchergebnisse anzeigen",
"Search results for '{{term}}'": "Ergebnisse für '{{term}}'",
"Close Search": "Suche schließen",
"Previous Result": "Vorheriges Ergebnis",
"Next Result": "Nächstes Ergebnis",
"Bookmarks": "Lesezeichen",
"Annotations": "Anmerkungen",
"Show Results": "Ergebnisse anzeigen",
"Clear search": "Suche löschen",
"Clear search history": "Suchverlauf löschen",
"Tap to Toggle Footer": "Tippen, um Fußzeile umzuschalten",
"Show Current Time": "Aktuelle Uhrzeit anzeigen",
"Use 24 Hour Clock": "24-Stunden-Format verwenden",
"Show Current Battery Status": "Aktuellen Akkustand anzeigen",
"Exported successfully": "Erfolgreich exportiert",
"Book exported successfully.": "Buch erfolgreich exportiert.",
"Failed to export the book.": "Buch konnte nicht exportiert werden.",
"Export Book": "Buch exportieren",
"Whole word:": "Ganzes Wort:",
"Date Published": "Veröffentlichungsdatum",
"Only for TTS:": "Nur für TTS:",
"Uploaded": "Hochgeladen",
"Downloaded": "Heruntergeladen",
"Deleted": "Gelöscht",
"Note:": "Notiz:",
"Time:": "Zeit:",
"Format Options": "Formatoptionen",
"Export Date": "Exportdatum",
"Chapter Titles": "Kapiteltitel",
"Chapter Separator": "Kapiteltrennzeichen",
"Highlights": "Markierungen",
"Note Date": "Notizdatum",
"Advanced": "Erweitert",
"Hide": "Ausblenden",
"Show": "Anzeigen",
"Use Custom Template": "Benutzerdefinierte Vorlage verwenden",
"Export Template": "Exportvorlage",
"Template Syntax:": "Vorlagensyntax:",
"Insert value": "Wert einfügen",
"Format date (locale)": "Datum formatieren (Gebietsschema)",
"Format date (custom)": "Datum formatieren (benutzerdefiniert)",
"Conditional": "Bedingung",
"Loop": "Schleife",
"Available Variables:": "Verfügbare Variablen:",
"Book title": "Buchtitel",
"Book author": "Buchautor",
"Export date": "Exportdatum",
"Array of chapters": "Array von Kapiteln",
"Chapter title": "Kapiteltitel",
"Array of annotations": "Array von Anmerkungen",
"Highlighted text": "Markierter Text",
"Annotation note": "Anmerkungsnotiz",
"Date Format Tokens:": "Datumsformat-Token:",
"Year (4 digits)": "Jahr (4 Ziffern)",
"Month (01-12)": "Monat (01-12)",
"Day (01-31)": "Tag (01-31)",
"Hour (00-23)": "Stunde (00-23)",
"Minute (00-59)": "Minute (00-59)",
"Second (00-59)": "Sekunde (00-59)",
"Show Source": "Quelle anzeigen",
"No content to preview": "Kein Inhalt zur Vorschau",
"Export": "Exportieren",
"Set Timeout": "Zeitlimit festlegen",
"Select Voice": "Stimme auswählen",
"Toggle Sticky Bottom TTS Bar": "Fixierte TTS-Leiste umschalten",
"Display what I'm reading on Discord": "Zeige was ich auf Discord lese",
"Show on Discord": "Auf Discord zeigen",
"Instant {{action}}": "Sofort {{action}}",
"Instant {{action}} Disabled": "Sofort {{action}} deaktiviert",
"Annotation": "Anmerkung",
"Reset Template": "Vorlage zurücksetzen",
"Annotation style": "Anmerkungsstil",
"Annotation color": "Anmerkungsfarbe",
"Annotation time": "Anmerkungszeit",
"AI": "KI",
"Are you sure you want to re-index this book?": "Möchten Sie dieses Buch wirklich neu indizieren?",
"Enable AI in Settings": "KI in den Einstellungen aktivieren",
"Index This Book": "Dieses Buch indizieren",
"Enable AI search and chat for this book": "KI-Suche und Chat für dieses Buch aktivieren",
"Start Indexing": "Indizierung starten",
"Indexing book...": "Buch wird indiziert...",
"Preparing...": "Vorbereitung...",
"Delete this conversation?": "Diese Unterhaltung löschen?",
"No conversations yet": "Noch keine Unterhaltungen",
"Start a new chat to ask questions about this book": "Starten Sie einen neuen Chat, um Fragen zu diesem Buch zu stellen",
"Rename": "Umbenennen",
"New Chat": "Neuer Chat",
"Chat": "Chat",
"Please enter a model ID": "Bitte geben Sie eine Modell-ID ein",
"Model not available or invalid": "Modell nicht verfügbar oder ungültig",
"Failed to validate model": "Modellvalidierung fehlgeschlagen",
"Couldn't connect to Ollama. Is it running?": "Verbindung zu Ollama fehlgeschlagen. Läuft es?",
"Invalid API key or connection failed": "Ungültiger API-Schlüssel oder Verbindung fehlgeschlagen",
"Connection failed": "Verbindung fehlgeschlagen",
"AI Assistant": "KI-Assistent",
"Enable AI Assistant": "KI-Assistent aktivieren",
"Provider": "Anbieter",
"Ollama (Local)": "Ollama (Lokal)",
"AI Gateway (Cloud)": "KI-Gateway (Cloud)",
"Ollama Configuration": "Ollama-Konfiguration",
"Refresh Models": "Modelle aktualisieren",
"AI Model": "KI-Modell",
"No models detected": "Keine Modelle erkannt",
"AI Gateway Configuration": "KI-Gateway-Konfiguration",
"Choose from a selection of high-quality, economical AI models. You can also bring your own model by selecting \"Custom Model\" below.": "Wählen Sie aus einer Auswahl hochwertiger, wirtschaftlicher KI-Modelle. Sie können auch Ihr eigenes Modell verwenden, indem Sie unten \"Benutzerdefiniertes Modell\" auswählen.",
"API Key": "API-Schlüssel",
"Get Key": "Schlüssel erhalten",
"Model": "Modell",
"Custom Model...": "Benutzerdefiniertes Modell...",
"Custom Model ID": "Benutzerdefinierte Modell-ID",
"Validate": "Validieren",
"Model available": "Modell verfügbar",
"Connection": "Verbindung",
"Test Connection": "Verbindung testen",
"Connected": "Verbunden",
"Custom Colors": "Benutzerdefinierte Farben",
"Color E-Ink Mode": "Farb-E-Ink-Modus",
"Reading Ruler": "Lese-Lineal",
"Enable Reading Ruler": "Lese-Lineal aktivieren",
"Lines to Highlight": "Hervorzuhebende Zeilen",
"Ruler Color": "Linealfarbe",
"Command Palette": "Befehlspalette",
"Search settings and actions...": "Einstellungen und Aktionen suchen...",
"No results found for": "Keine Ergebnisse gefunden für",
"Type to search settings and actions": "Tippen, um Einstellungen und Aktionen zu suchen",
"Recent": "Zuletzt verwendet",
"navigate": "navigieren",
"select": "auswählen",
"close": "schließen",
"Search Settings": "Einstellungen suchen",
"Page Margins": "Seitenränder",
"AI Provider": "KI-Anbieter",
"Ollama URL": "Ollama-URL",
"Ollama Model": "Ollama-Modell",
"AI Gateway Model": "AI Gateway-Modell",
"Actions": "Aktionen",
"Navigation": "Navigation",
"Set status for {{count}} book(s)_one": "Status für {{count}} Buch festlegen",
"Set status for {{count}} book(s)_other": "Status für {{count}} Bücher festlegen",
"Mark as Unread": "Als ungelesen markieren",
"Mark as Finished": "Als beendet markieren",
"Finished": "Beendet",
"Unread": "Ungelesen",
"Clear Status": "Status löschen",
"Status": "Status",
"Loading": "Laden...",
"Exit Paragraph Mode": "Absatzmodus verlassen",
"Paragraph Mode": "Absatzmodus",
"Embedding Model": "Einbettungsmodell",
"{{count}} book(s) synced_one": "{{count}} Buch synchronisiert",
"{{count}} book(s) synced_other": "{{count}} Bücher synchronisiert",
"Unable to start RSVP": "RSVP kann nicht gestartet werden",
"RSVP not supported for PDF": "RSVP wird für PDF nicht unterstützt",
"Select Chapter": "Kapitel auswählen",
"Context": "Kontext",
"Ready": "Bereit",
"Chapter Progress": "Kapitelfortschritt",
"words": "Wörter",
"{{time}} left": "{{time}} übrig",
"Reading progress": "Lesefortschritt",
"Skip back 15 words": "15 Wörter zurück",
"Back 15 words (Shift+Left)": "15 Wörter zurück (Shift+Links)",
"Pause (Space)": "Pause (Leertaste)",
"Play (Space)": "Abspielen (Leertaste)",
"Skip forward 15 words": "15 Wörter vor",
"Forward 15 words (Shift+Right)": "15 Wörter vor (Shift+Rechts)",
"Decrease speed": "Geschwindigkeit verringern",
"Slower (Left/Down)": "Langsamer (Links/Unten)",
"Increase speed": "Geschwindigkeit erhöhen",
"Faster (Right/Up)": "Schneller (Rechts/Oben)",
"Start RSVP Reading": "RSVP-Lesen starten",
"Choose where to start reading": "Wählen Sie, wo das Lesen beginnen soll",
"From Chapter Start": "Vom Kapitelanfang",
"Start reading from the beginning of the chapter": "Ab dem Anfang des Kapitels lesen",
"Resume": "Fortsetzen",
"Continue from where you left off": "Dort fortfahren, wo Sie aufgehört haben",
"From Current Page": "Von der aktuellen Seite",
"Start from where you are currently reading": "Dort beginnen, wo Sie gerade lesen",
"From Selection": "Von der Auswahl",
"Speed Reading Mode": "Schnelllesemodus",
"Scroll left": "Nach links scrollen",
"Scroll right": "Nach rechts scrollen",
"Library Sync Progress": "Bibliotheks-Synchronisierungsfortschritt",
"Back to library": "Zurück zur Bibliothek",
"Group by...": "Gruppieren nach...",
"Export as Plain Text": "Als reinen Text exportieren",
"Export as Markdown": "Als Markdown exportieren",
"Show Page Navigation Buttons": "Navigationsschaltflächen",
"Page {{number}}": "Seite {{number}}",
"highlight": "Hervorhebung",
"underline": "Unterstreichung",
"squiggly": "geschlängelt",
"red": "rot",
"violet": "violett",
"blue": "blau",
"green": "grün",
"yellow": "gelb",
"Select {{style}} style": "Stil {{style}} auswählen",
"Select {{color}} color": "Farbe {{color}} auswählen",
"Close Book": "Buch schließen",
"Speed Reading": "Schnelllesen",
"Close Speed Reading": "Schnelllesen schließen",
"Authors": "Autoren",
"Books": "Bücher",
"Groups": "Gruppen",
"Back to TTS Location": "Zurück zur TTS-Position",
"Metadata": "Metadaten",
"Image viewer": "Bildbetrachter",
"Previous Image": "Vorheriges Bild",
"Next Image": "Nächstes Bild",
"Zoomed": "Gezoomt",
"Zoom level": "Zoomstufe",
"Table viewer": "Tabellenbetrachter",
"Unable to connect to Readwise. Please check your network connection.": "Verbindung zu Readwise nicht möglich. Bitte überprüfen Sie Ihre Netzwerkverbindung.",
"Invalid Readwise access token": "Ungültiges Readwise-Zugriffstoken",
"Disconnected from Readwise": "Von Readwise getrennt",
"Never": "Nie",
"Sync Enabled": "Synchronisierung aktiviert",
"Disconnect": "Trennen",
"Connect your Readwise account to sync highlights.": "Verbinden Sie Ihr Readwise-Konto, um Highlights zu synchronisieren.",
"Get your access token at": "Holen Sie sich Ihr Zugriffstoken unter",
"Access Token": "Zugriffstoken",
"Paste your Readwise access token": "Fügen Sie Ihr Readwise-Zugriffstoken ein",
"Readwise Sync": "Readwise-Synchronisierung",
"Push Highlights": "Highlights übertragen",
"Highlights synced to Readwise": "Highlights mit Readwise synchronisiert",
"Readwise sync failed: no internet connection": "Readwise-Synchronisierung fehlgeschlagen: Keine Internetverbindung",
"Readwise sync failed: {{error}}": "Readwise-Synchronisierung fehlgeschlagen: {{error}}",
"System Screen Brightness": "System-Bildschirmhelligkeit",
"Page:": "Seite:",
"Page: {{number}}": "Seite: {{number}}",
"Annotation page number": "Seitenzahl der Anmerkung",
"Translating...": "Übersetzen...",
"Show Battery Percentage": "Akkustand in Prozent anzeigen",
"Hide Scrollbar": "Scrollleiste ausblenden",
"Skip to last reading position": "Zur letzten Leseposition springen",
"Show context": "Kontext anzeigen",
"Hide context": "Kontext ausblenden",
"Decrease font size": "Schriftgröße verkleinern",
"Increase font size": "Schriftgröße vergrößern",
"Focus": "Fokus",
"Theme color": "Themenfarbe",
"Import failed": "Import fehlgeschlagen",
"Available Formatters:": "Verfügbare Formatierer:",
"Format date": "Datum formatieren",
"Markdown block quote (> per line)": "Markdown-Blockzitat (> pro Zeile)",
"Newlines to <br>": "Zeilenumbrüche zu <br>",
"Change case": "Groß-/Kleinschreibung ändern",
"Trim whitespace": "Leerzeichen entfernen",
"Truncate to n characters": "Auf n Zeichen kürzen",
"Replace text": "Text ersetzen",
"Fallback value": "Ersatzwert",
"Get length": "Länge ermitteln",
"First/last element": "Erstes/letztes Element",
"Join array": "Array verbinden",
"Backup failed: {{error}}": "Sicherung fehlgeschlagen: {{error}}",
"Select Backup": "Sicherung auswählen",
"Restore failed: {{error}}": "Wiederherstellung fehlgeschlagen: {{error}}",
"Backup & Restore": "Sichern & Wiederherstellen",
"Create a backup of your library or restore from a previous backup. Restoring will merge with your current library.": "Erstellen Sie eine Sicherung Ihrer Bibliothek oder stellen Sie eine frühere Sicherung wieder her. Die Wiederherstellung wird mit Ihrer aktuellen Bibliothek zusammengeführt.",
"Backup Library": "Bibliothek sichern",
"Restore Library": "Bibliothek wiederherstellen",
"Creating backup...": "Sicherung wird erstellt...",
"Restoring library...": "Bibliothek wird wiederhergestellt...",
"{{current}} of {{total}} items": "{{current}} von {{total}} Elementen",
"Backup completed successfully!": "Sicherung erfolgreich abgeschlossen!",
"Restore completed successfully!": "Wiederherstellung erfolgreich abgeschlossen!",
"Your library has been saved to the selected location.": "Ihre Bibliothek wurde am ausgewählten Ort gespeichert.",
"{{added}} books added, {{updated}} books updated.": "{{added}} Bücher hinzugefügt, {{updated}} Bücher aktualisiert.",
"Operation failed": "Vorgang fehlgeschlagen",
"Loading library...": "Bibliothek wird geladen...",
"{{count}} books refreshed_one": "{{count}} Buch aktualisiert",
"{{count}} books refreshed_other": "{{count}} Bücher aktualisiert",
"Failed to refresh metadata": "Metadaten-Aktualisierung fehlgeschlagen",
"Refresh Metadata": "Metadaten aktualisieren",
"Keyboard Shortcuts": "Tastaturkürzel",
"View all keyboard shortcuts": "Alle Tastaturkürzel anzeigen",
"Switch Sidebar Tab": "Seitenleisten-Tab wechseln",
"Toggle Notebook": "Notizbuch ein-/ausblenden",
"Search in Book": "Im Buch suchen",
"Toggle Scroll Mode": "Scrollmodus umschalten",
"Toggle Select Mode": "Auswahlmodus umschalten",
"Toggle Bookmark": "Lesezeichen umschalten",
"Toggle Text to Speech": "Sprachausgabe umschalten",
"Play / Pause TTS": "Sprachausgabe abspielen / pausieren",
"Toggle Paragraph Mode": "Absatzmodus umschalten",
"Highlight Selection": "Auswahl hervorheben",
"Underline Selection": "Auswahl unterstreichen",
"Annotate Selection": "Auswahl kommentieren",
"Search Selection": "Auswahl suchen",
"Copy Selection": "Auswahl kopieren",
"Translate Selection": "Auswahl übersetzen",
"Dictionary Lookup": "Im Wörterbuch nachschlagen",
"Read Aloud Selection": "Auswahl vorlesen",
"Proofread Selection": "Auswahl korrekturlesen",
"Open Settings": "Einstellungen öffnen",
"Open Command Palette": "Befehlspalette öffnen",
"Show Keyboard Shortcuts": "Tastaturkürzel anzeigen",
"Open Books": "Bücher öffnen",
"Toggle Fullscreen": "Vollbild umschalten",
"Close Window": "Fenster schließen",
"Quit App": "App beenden",
"Go Left / Previous Page": "Nach links / Vorherige Seite",
"Go Right / Next Page": "Nach rechts / Nächste Seite",
"Go Up": "Nach oben",
"Go Down": "Nach unten",
"Previous Chapter": "Vorheriges Kapitel",
"Next Chapter": "Nächstes Kapitel",
"Scroll Half Page Down": "Halbe Seite nach unten scrollen",
"Scroll Half Page Up": "Halbe Seite nach oben scrollen",
"Save Note": "Notiz speichern",
"Single Section Scroll": "Einzelabschnitt-Scrollen",
"General": "Allgemein",
"Text to Speech": "Sprachausgabe",
"Zoom": "Zoom",
"Window": "Fenster",
"Your Bookshelf": "Dein Bücherregal",
"TTS": "Sprachausgabe",
"Media Info": "Medieninfo",
"Update Frequency": "Aktualisierungshäufigkeit",
"Every Sentence": "Jeden Satz",
"Every Paragraph": "Jeden Absatz",
"Every Chapter": "Jedes Kapitel",
"TTS Media Info Update Frequency": "TTS-Medieninfo-Aktualisierungshäufigkeit",
"Select reading speed": "Lesegeschwindigkeit wählen",
"Drag to seek": "Ziehen zum Suchen",
"Punctuation Delay": "Satzzeichen-Verzögerung",
"Please confirm that this OPDS connection will be proxied through Readest servers on the web app before continuing.": "Bitte bestätigen Sie, dass diese OPDS-Verbindung über Readest-Server in der Web-App weitergeleitet wird, bevor Sie fortfahren.",
"Custom Headers (optional)": "Benutzerdefinierte Header (optional)",
"Add one header per line using \"Header-Name: value\".": "Fügen Sie pro Zeile einen Header im Format \"Header-Name: value\" hinzu.",
"I understand this OPDS connection will be proxied through Readest servers on the web app. If I do not trust Readest with these credentials or headers, I should use the native app instead.": "Ich verstehe, dass diese OPDS-Verbindung über Readest-Server in der Web-App weitergeleitet wird. Wenn ich Readest meine Anmeldedaten oder Header nicht anvertraue, sollte ich stattdessen die native App verwenden.",
"Unable to connect to Hardcover. Please check your network connection.": "Verbindung zu Hardcover nicht möglich. Bitte überprüfen Sie Ihre Netzwerkverbindung.",
"Invalid Hardcover API token": "Ungültiges Hardcover-API-Token",
"Disconnected from Hardcover": "Von Hardcover getrennt",
"Connect your Hardcover account to sync reading progress and notes.": "Verbinden Sie Ihr Hardcover-Konto, um Lesefortschritt und Notizen zu synchronisieren.",
"Get your API token from hardcover.app → Settings → API.": "Holen Sie Ihr API-Token von hardcover.app → Einstellungen → API.",
"API Token": "API-Token",
"Paste your Hardcover API token": "Fügen Sie Ihr Hardcover-API-Token ein",
"Hardcover Sync": "Hardcover-Synchronisierung",
"Push Notes": "Notizen übertragen",
"No annotations or excerpts to sync for this book.": "Keine Anmerkungen oder Auszüge zum Synchronisieren für dieses Buch.",
"Configure Hardcover in Settings first.": "Konfigurieren Sie zuerst Hardcover in den Einstellungen.",
"No new Hardcover note changes to sync.": "Keine neuen Hardcover-Notizänderungen zum Synchronisieren.",
"Hardcover synced: {{inserted}} new, {{updated}} updated, {{skipped}} unchanged": "Hardcover synchronisiert: {{inserted}} neu, {{updated}} aktualisiert, {{skipped}} unverändert",
"Hardcover notes sync failed: {{error}}": "Hardcover-Notizsynchronisierung fehlgeschlagen: {{error}}",
"Reading progress synced to Hardcover": "Lesefortschritt mit Hardcover synchronisiert",
"Hardcover progress sync failed: {{error}}": "Hardcover-Fortschrittssynchronisierung fehlgeschlagen: {{error}}",
"ISBN": "ISBN",
"Keep existing source identifier": "Vorhandene Quellkennung beibehalten",
"Toggle Toolbar": "Symbolleiste umschalten",
"Split Hyphens": "Bindestriche trennen",
"Apply Theme Colors to PDF": "Designfarben auf PDF anwenden",
"Name": "Name",
"Remove": "Entfernen",
"Edit OPDS Catalog": "OPDS-Katalog bearbeiten",
"Save Changes": "Änderungen speichern",
"Use Book Layout": "Buchlayout verwenden",
"End of this section. Continue to the next.": "Ende dieses Abschnitts. Weiter zum nächsten.",
"Auto-download": "Automatisch herunterladen",
"Auto-download new items": "Neue Einträge automatisch herunterladen",
"Automatically download new publications when the app syncs": "Neue Veröffentlichungen automatisch herunterladen, wenn die App synchronisiert",
"{{count}} new item(s) downloaded from OPDS_one": "{{count}} neuer Eintrag von OPDS heruntergeladen",
"{{count}} new item(s) downloaded from OPDS_other": "{{count}} neue Einträge von OPDS heruntergeladen",
"Failed to sync {{count}} OPDS catalog(s)_one": "Synchronisation von {{count}} OPDS-Katalog fehlgeschlagen",
"Failed to sync {{count}} OPDS catalog(s)_other": "Synchronisation von {{count}} OPDS-Katalogen fehlgeschlagen",
"Last synced {{when}}": "Zuletzt synchronisiert {{when}}",
"Failed downloads": "Fehlgeschlagene Downloads",
"No failed downloads": "Keine fehlgeschlagenen Downloads",
"Attempts: {{count}}_one": "Versuche: {{count}}",
"Attempts: {{count}}_other": "Versuche: {{count}}",
"Skip": "Überspringen",
"Skip all": "Alle überspringen",
"Retry all": "Alle erneut versuchen",
"{{count}} failed_one": "{{count}} fehlgeschlagen",
"{{count}} failed_other": "{{count}} fehlgeschlagen",
"(none)": "(keine)",
"Identifiers": "Bezeichner",
"Read (Stream)": "Lesen (Stream)",
"Failed to start stream": "Stream konnte nicht gestartet werden",
"No dictionaries enabled": "Keine Wörterbücher aktiviert",
"Enable a dictionary in Settings → Language → Dictionaries.": "Aktiviere ein Wörterbuch unter Einstellungen → Sprache → Wörterbücher.",
"Wiktionary": "Wiktionary",
"Drag to reorder": "Zum Neuanordnen ziehen",
"Built-in": "Integriert",
"Bundle is missing on this device. Re-import to use it.": "Bundle fehlt auf diesem Gerät. Erneut importieren, um es zu verwenden.",
"This dictionary format is not supported.": "Dieses Wörterbuchformat wird nicht unterstützt.",
"MDict": "MDict",
"StarDict": "StarDict",
"Imported {{count}} dictionary_one": "{{count}} Wörterbuch importiert",
"Imported {{count}} dictionary_other": "{{count}} Wörterbücher importiert",
"Skipped incomplete bundles: {{names}}": "Unvollständige Bundles übersprungen: {{names}}",
"Failed to import dictionary: {{message}}": "Wörterbuch-Import fehlgeschlagen: {{message}}",
"Dictionaries": "Wörterbücher",
"Delete Dictionary": "Wörterbuch löschen",
"Importing…": "Wird importiert …",
"Import Dictionary": "Wörterbuch importieren",
"No dictionaries available.": "Keine Wörterbücher verfügbar.",
"StarDict bundles need .ifo, .idx, and .dict.dz files (.syn optional).": "StarDict-Bundles benötigen .ifo-, .idx- und .dict.dz-Dateien (.syn optional).",
"Select all the bundle files together when importing.": "Wähle beim Import alle Bundle-Dateien zusammen aus.",
"Manage Dictionaries": "Wörterbücher verwalten",
"Select Dictionary Files": "Wörterbuchdateien auswählen",
"Read on Wikipedia →": "Auf Wikipedia lesen →",
"This link can't be opened": "Dieser Link kann nicht geöffnet werden",
"The annotation link is missing required information. The original link may have been truncated.": "Dem Annotationslink fehlen erforderliche Informationen. Der ursprüngliche Link wurde möglicherweise abgeschnitten.",
"Go to Readest": "Zu Readest",
"Open-source ebook reader for everyone, on every device.": "Open-Source-E-Book-Reader für alle, auf jedem Gerät.",
"Open in Readest": "In Readest öffnen",
"If Readest didn't open automatically, choose an option below:": "Falls Readest nicht automatisch geöffnet wurde, wähle eine Option:",
"Continue reading where you left off.": "Lies dort weiter, wo du aufgehört hast.",
"Readest logo": "Readest-Logo",
"Opening Readest...": "Readest wird geöffnet …",
"Open in Readest app": "In der Readest-App öffnen",
"Continue in browser": "Im Browser fortfahren",
"Don't have Readest?": "Du hast Readest noch nicht?",
"Book not in your library": "Buch nicht in deiner Bibliothek",
"Share Book": "Buch teilen",
"Could not create share link": "Freigabelink konnte nicht erstellt werden",
"Link copied": "Link kopiert",
"Could not copy link": "Link konnte nicht kopiert werden",
"Share revoked": "Freigabe widerrufen",
"Could not revoke share": "Freigabe konnte nicht widerrufen werden",
"Uploading book…": "Buch wird hochgeladen…",
"Generating…": "Wird erstellt…",
"Generate share link": "Freigabelink erstellen",
"Share URL": "Freigabelink",
"Copy link": "Link kopieren",
"Copied": "Kopiert",
"Share via…": "Teilen über…",
"Expires {{date}}": "Läuft ab am {{date}}",
"Revoking…": "Wird widerrufen…",
"Revoke share": "Freigabe widerrufen",
"Sign in to share books": "Melde dich an, um Bücher zu teilen",
"Expiring soon": "Läuft bald ab",
"Missing share token": "Freigabe-Token fehlt",
"Could not add to your library": "Konnte nicht zu deiner Bibliothek hinzugefügt werden",
"This share link is no longer available": "Dieser Freigabelink ist nicht mehr verfügbar",
"The original link may have expired or been revoked.": "Der ursprüngliche Link ist möglicherweise abgelaufen oder wurde widerrufen.",
"Get Readest": "Readest holen",
"Loading shared book…": "Geteiltes Buch wird geladen…",
"Adding…": "Wird hinzugefügt…",
"Add to my library": "Zu meiner Bibliothek hinzufügen",
"Could not load your shares": "Deine Freigaben konnten nicht geladen werden",
"Revoked": "Widerrufen",
"Expired": "Abgelaufen",
"Shared books": "Geteilte Bücher",
"You haven't shared any books yet": "Du hast noch keine Bücher geteilt",
"Open a book and tap Share to send it to a friend.": "Öffne ein Buch und tippe auf „Teilen“, um es an einen Freund zu senden.",
"{{count}} active_one": "{{count}} aktiv",
"{{count}} active_other": "{{count}} aktiv",
"{{count}} downloads_one": "{{count}} Download",
"{{count}} downloads_other": "{{count}} Downloads",
"starts at saved page": "beginnt auf gespeicherter Seite",
"More actions": "Weitere Aktionen",
"Loading…": "Wird geladen…",
"Load more": "Mehr laden",
"Could not load shared book": "Geteiltes Buch konnte nicht geladen werden",
"The share link is missing required information.": "Dem Freigabelink fehlen erforderliche Informationen.",
"Please check your connection and try again.": "Bitte überprüfen Sie Ihre Verbindung und versuchen Sie es erneut.",
"Sign in to import shared books": "Melde dich an, um geteilte Bücher zu importieren",
"Already in your library": "Bereits in deiner Bibliothek",
"Added to your library": "Zu deiner Bibliothek hinzugefügt",
"Could not import shared book": "Geteiltes Buch konnte nicht importiert werden",
"Manage Shared Links": "Freigabelinks verwalten",
"Expires in": "Läuft ab in",
"{{count}} days_one": "1 Tag",
"{{count}} days_other": "{{count}} Tage",
"Expires in {{count}} days_one": "Läuft in 1 Tag ab",
"Expires in {{count}} days_other": "Läuft in {{count}} Tagen ab",
"Expires in {{count}} hours_one": "Läuft in 1 Stunde ab",
"Expires in {{count}} hours_other": "Läuft in {{count}} Stunden ab",
"Open in app": "In der App öffnen",
"Shared with you": "Mit dir geteilt",
"Downloading… {{percent}}%": "Wird heruntergeladen… {{percent}}%",
"Import progress": "Importfortschritt",
"Share reading progress": "Lesefortschritt teilen",
"Includes your reading progress": "Enthält deinen Lesefortschritt",
"Synced {{time}}": "Synchronisiert {{time}}",
"Sync Info": "Synchronisierungs-Info",
"Last Synced": "Zuletzt synchronisiert",
"DICT": "DICT",
"Slob": "Slob",
"DICT bundles need a .index file and a .dict.dz file.": "DICT-Bundles benötigen eine .index-Datei und eine .dict.dz-Datei.",
"Slob bundles need a .slob file.": "Slob-Bundles benötigen eine .slob-Datei.",
"URL template must start with http(s):// and contain %WORD%.": "URL-Vorlage muss mit http(s):// beginnen und %WORD% enthalten.",
"Web": "Web",
"Add Web Search": "Websuche hinzufügen",
"Edit Web Search": "Websuche bearbeiten",
"e.g. Google": "z. B. Google",
"URL Template": "URL-Vorlage",
"Use %WORD% where the looked-up word should appear.": "Verwenden Sie %WORD%, wo das gesuchte Wort erscheinen soll.",
"Open the search result in your browser:": "Öffne das Suchergebnis in deinem Browser:",
"Open in {{name}}": "In {{name}} öffnen",
"Book Fingerprint": "Buch-Fingerabdruck",
"Search the web": "Im Web suchen",
"App deeplink (readest://)": "App-Deeplink (readest://)",
"Universal web link (https://)": "Universeller Weblink (https://)",
"Name cannot be empty.": "Name darf nicht leer sein.",
"Replaced {{count}} existing dictionary_one": "{{count}} bestehendes Wörterbuch ersetzt",
"Replaced {{count}} existing dictionary_other": "{{count}} bestehende Wörterbücher ersetzt",
"Cancel Edit": "Bearbeiten abbrechen",
"Edit Dictionary": "Wörterbuch bearbeiten",
"MDict bundles use .mdx files; companion .mdd and .css files are optional.": "MDict-Bundles verwenden .mdx-Dateien; begleitende .mdd- und .css-Dateien sind optional.",
"Dictionary name": "Wörterbuchname",
"This audio can't play here — the dictionary uses an outdated format. Try one with Opus, MP3, or WAV audio.": "Diese Audio kann hier nicht abgespielt werden — das Wörterbuch verwendet ein veraltetes Format. Probieren Sie ein Wörterbuch mit Opus-, MP3- oder WAV-Audio.",
"File uploaded: {{title}}": "Datei hochgeladen: {{title}}",
"File downloaded: {{title}}": "Datei heruntergeladen: {{title}}",
"Deleted cloud copy of the file: {{title}}": "Cloud-Kopie der Datei gelöscht: {{title}}",
"Failed to upload file: {{title}}": "Fehler beim Hochladen der Datei: {{title}}",
"Failed to download file: {{title}}": "Fehler beim Herunterladen der Datei: {{title}}",
"Failed to delete cloud copy of the file: {{title}}": "Fehler beim Löschen der Cloud-Kopie der Datei: {{title}}",
"{{percentage}}% used": "{{percentage}}% verbraucht",
"Set PIN…": "PIN festlegen…",
"Require a 4-digit PIN to open Readest": "Eine 4-stellige PIN zum Öffnen von Readest verlangen",
"Change PIN…": "PIN ändern…",
"Disable PIN…": "PIN deaktivieren…",
"Sync passphrase ready": "Sync-Passphrase bereit",
"This permanently deletes the encrypted credentials we sync (e.g., OPDS catalog passwords) on every device. Local copies are preserved. You will need to re-enter the sync passphrase or set a new one. Continue?": "Dies löscht dauerhaft die verschlüsselten Anmeldedaten, die wir auf jedem Gerät synchronisieren (z. B. OPDS-Katalog-Passwörter). Lokale Kopien bleiben erhalten. Sie müssen die Sync-Passphrase erneut eingeben oder eine neue festlegen. Fortfahren?",
"Sync passphrase forgotten — all encrypted fields cleared": "Sync-Passphrase vergessen — alle verschlüsselten Felder wurden gelöscht",
"Sync passphrase": "Sync-Passphrase",
"Set passphrase": "Passphrase festlegen",
"Forgot passphrase": "Passphrase vergessen",
"Incorrect PIN": "Falsche PIN",
"App locked": "App gesperrt",
"Enter your PIN": "PIN eingeben",
"Readest is locked. Enter your 4-digit PIN to continue.": "Readest ist gesperrt. Geben Sie Ihre 4-stellige PIN ein, um fortzufahren.",
"PIN code": "PIN-Code",
"Forgetting your PIN locks you out of this device. You'll need to clear the app's data to reset it.": "Wenn Sie Ihre PIN vergessen, sperren Sie sich von diesem Gerät aus. Sie müssen die App-Daten löschen, um sie zurückzusetzen.",
"Passphrase must be at least 8 characters": "Die Passphrase muss mindestens 8 Zeichen lang sein",
"Passphrases do not match": "Die Passphrasen stimmen nicht überein",
"Set sync passphrase": "Sync-Passphrase festlegen",
"Enter sync passphrase": "Sync-Passphrase eingeben",
"A sync passphrase encrypts your sensitive fields (like OPDS catalog credentials) before they sync. We never see this passphrase. Pick something memorable — there is no recovery without it.": "Eine Sync-Passphrase verschlüsselt Ihre sensiblen Felder (wie OPDS-Katalog-Anmeldedaten), bevor sie synchronisiert werden. Wir sehen diese Passphrase nie. Wählen Sie etwas Einprägsames — ohne sie ist keine Wiederherstellung möglich.",
"Enter the sync passphrase you set on another device to decrypt your synced credentials.": "Geben Sie die Sync-Passphrase ein, die Sie auf einem anderen Gerät festgelegt haben, um Ihre synchronisierten Anmeldedaten zu entschlüsseln.",
"Confirm passphrase": "Passphrase bestätigen",
"Unlock": "Entsperren",
"Set PIN": "PIN festlegen",
"Change PIN": "PIN ändern",
"Disable PIN": "PIN deaktivieren",
"Enter your current PIN, then choose a new 4-digit PIN.": "Geben Sie Ihre aktuelle PIN ein und wählen Sie dann eine neue 4-stellige PIN.",
"Enter your current PIN to disable the app lock.": "Geben Sie Ihre aktuelle PIN ein, um die App-Sperre zu deaktivieren.",
"PIN must be {{length}} digits": "Die PIN muss aus {{length}} Ziffern bestehen",
"PINs do not match": "PINs stimmen nicht überein",
"Current PIN": "Aktuelle PIN",
"New PIN": "Neue PIN",
"Confirm new PIN": "Neue PIN bestätigen",
"Manage Sync": "Synchronisierung verwalten",
"Imported book files and library metadata": "Importierte Buchdateien und Bibliotheksmetadaten",
"Last-read position, bookmarks, and per-book preferences": "Zuletzt gelesene Position, Lesezeichen und buchspezifische Einstellungen",
"Highlights and notes": "Hervorhebungen und Notizen",
"Fonts": "Schriftarten",
"Custom font files": "Benutzerdefinierte Schriftdateien",
"Backgrounds": "Hintergründe",
"Custom background textures": "Benutzerdefinierte Hintergrundtexturen",
"OPDS catalogs": "OPDS-Kataloge",
"Saved catalog URLs and (encrypted) credentials": "Gespeicherte Katalog-URLs und (verschlüsselte) Anmeldedaten",
"Wrong sync passphrase — synced credentials could not be decrypted": "Falsche Sync-Passphrase — synchronisierte Anmeldedaten konnten nicht entschlüsselt werden",
"Sync passphrase data on the server was reset. Re-encrypting your credentials under the new passphrase…": "Sync-Passphrase auf dem Server wurde zurückgesetzt. Anmeldedaten werden mit der neuen Passphrase neu verschlüsselt…",
"Failed to decrypt synced credentials": "Synchronisierte Anmeldedaten konnten nicht entschlüsselt werden",
"Imported dictionary bundles and settings": "Importierte Wörterbuch-Pakete und Einstellungen",
"Choose what syncs across your devices. Disabling a category stops this device from sending or receiving rows of that kind. Anything already on the server is left alone, re-enabling resumes from where you stopped.": "Lege fest, was zwischen deinen Geräten synchronisiert wird. Beim Deaktivieren einer Kategorie sendet und empfängt dieses Gerät keine Einträge dieser Art mehr. Bereits auf dem Server vorhandene Daten bleiben unverändert, beim erneuten Aktivieren wird dort fortgesetzt, wo du aufgehört hast.",
"Data Sync": "Datensynchronisierung",
"Manage Fonts": "Schriftarten verwalten",
"App settings": "App-Einstellungen",
"Theme, highlight colours, integrations (KOSync, Readwise, Hardcover), and dictionary order": "Design, Hervorhebungsfarben, Integrationen (KOSync, Readwise, Hardcover) und Wörterbuchreihenfolge",
"Required while Dictionaries sync is enabled": "Erforderlich, solange die Synchronisierung von Wörterbüchern aktiviert ist",
"Resets in {{hours}} hr {{minutes}} min": "Zurückgesetzt in {{hours}} Std. {{minutes}} Min.",
"Pick a 4-digit PIN. You will need to enter it every time you open Readest. There is no way to recover a forgotten PIN. Clearing the app data is the only way to reset it.": "Wählen Sie eine 4-stellige PIN. Sie müssen sie jedes Mal eingeben, wenn Sie Readest öffnen. Eine vergessene PIN kann nicht wiederhergestellt werden. Das Löschen der App-Daten ist die einzige Möglichkeit, sie zurückzusetzen.",
"Credentials": "Anmeldedaten",
"Tokens, usernames, and passwords for OPDS, KOReader, Hardcover, and Readwise. When disabled, credentials remain on this device only and are never uploaded.": "Tokens, Benutzernamen und Passwörter für OPDS, KOReader, Hardcover und Readwise. Wenn deaktiviert, bleiben die Anmeldedaten nur auf diesem Gerät und werden nie hochgeladen.",
"Sensitive synced fields are encrypted before upload. Set a passphrase now or later when encryption is needed.": "Sensible synchronisierte Felder werden vor dem Hochladen verschlüsselt. Lege jetzt oder später eine Passphrase fest, wenn die Verschlüsselung benötigt wird.",
"Saved to this account. You will be prompted for it when decrypting credentials.": "In diesem Konto gespeichert. Du wirst beim Entschlüsseln von Anmeldedaten danach gefragt.",
"Reading progress on this device differs from \"{{deviceName}}\".": "Der Lesefortschritt auf diesem Gerät weicht von „{{deviceName}}“ ab.",
"This device": "Dieses Gerät",
"Red": "Rot",
"Yellow": "Gelb",
"Green": "Grün",
"Blue": "Blau",
"Violet": "Violett",
"Edit color": "Farbe bearbeiten",
"Add custom color": "Benutzerdefinierte Farbe hinzufügen",
"Add label": "Beschriftung hinzufügen",
"Choose color": "Farbe auswählen",
"Connected to Hardcover. Last synced {{time}}.": "Mit Hardcover verbunden. Zuletzt synchronisiert {{time}}.",
"Integrations": "Integrationen",
"Hardcover": "Hardcover",
"Connected to Readwise. Last synced {{time}}.": "Mit Readwise verbunden. Zuletzt synchronisiert {{time}}.",
"Readwise": "Readwise",
"Browse and download books from online catalogs.": "Bücher aus Online-Katalogen durchsuchen und herunterladen.",
"Connected as {{user}}": "Verbunden als {{user}}",
"Not connected": "Nicht verbunden",
"{{count}} catalog_one": "{{count}} Katalog",
"{{count}} catalog_other": "{{count}} Kataloge",
"No catalogs": "Keine Kataloge",
"Connect Readest to external services for sync, highlights, and catalogs.": "Verbinden Sie Readest mit externen Diensten für Synchronisierung, Markierungen und Kataloge.",
"Reading Sync": "Lese-Synchronisierung",
"Content Sources": "Inhaltsquellen",
"Scroll tabs": "Tabs scrollen",
"Catalog actions": "Katalogaktionen",
"File Content": "Dateiinhalt",
"Start your library": "Starten Sie Ihre Bibliothek",
"Pick a book from your device to add it to your library.": "Wählen Sie ein Buch von Ihrem Gerät, um es Ihrer Bibliothek hinzuzufügen.",
"Drop a book anywhere on this window, or pick one from your computer.": "Ziehen Sie ein Buch an eine beliebige Stelle dieses Fensters oder wählen Sie eines von Ihrem Computer aus.",
"Sign in to sync your library": "Melden Sie sich an, um Ihre Bibliothek zu synchronisieren"
}
@@ -102,11 +102,9 @@
"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",
"Override Book Font": "Παράκαμψη γραμματοσειράς βιβλίου",
"Apply to All Books": "Εφαρμογή σε όλα τα βιβλία",
"Apply to This Book": "Εφαρμογή σε αυτό το βιβλίο",
@@ -173,9 +171,6 @@
"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": "Υποστήριξη κοινότητας",
@@ -188,7 +183,6 @@
"RTL Direction": "Κατεύθυνση RTL",
"Maximum Column Height": "Μέγιστο ύψος στήλης",
"Maximum Column Width": "Μέγιστο πλάτος στήλης",
"Continuous Scroll": "Συνεχής κύλιση",
"Fullscreen": "Πλήρης οθόνη",
"No supported files found. Supported formats: {{formats}}": "Δεν βρέθηκαν υποστηριζόμενα αρχεία. Υποστηριζόμενες μορφές: {{formats}}",
"Drop to Import Books": "Ρίξτε για εισαγωγή βιβλίων",
@@ -304,7 +298,6 @@
"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}} λεπτά απομένουν στο κεφάλαιο",
@@ -338,11 +331,11 @@
"Fit": "Προσαρμογή",
"Reset {{settings}}": "Επαναφορά {{settings}}",
"Reset Settings": "Επαναφορά ρυθμίσεων",
"{{count}} pages left in chapter_one": "Μένει {{count}} σελίδα",
"{{count}} pages left in chapter_other": "Μένουν {{count}} σελίδες",
"{{count}} pages left in chapter_one": "<1>Μένει </1><0>{{count}}</0><1> σελίδα</1>",
"{{count}} pages left in chapter_other": "<1>Μένουν </1><0>{{count}}</0><1> σελίδες</1>",
"Show Remaining Pages": "Εμφάνιση υπολοίπων",
"Source Han Serif CN VF": "Source Han Serif",
"Huiwen-mincho": "Huiwen Mincho",
"Source Han Serif CN": "Source Han Serif",
"Huiwen-MinchoGBK": "Huiwen Mincho",
"KingHwa_OldSong": "KingHwa Song",
"Manage Subscription": "Διαχείριση συνδρομής",
"Coming Soon": "Έρχεται σύντομα",
@@ -466,14 +459,12 @@
"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": "Λήψη μόνο αλλαγών",
"Checksum Method": "Μέθοδος ελέγχου",
"File Content (recommended)": "Περιεχόμενο αρχείου (συνιστάται)",
"File Name": "Όνομα αρχείου",
"Device Name": "Όνομα συσκευής",
"Connect to your KOReader Sync server.": "Συνδεθείτε στον διακομιστή συγχρονισμού KOReader.",
@@ -484,10 +475,7 @@
"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}}%)",
@@ -602,11 +590,10 @@
"Size": "Μέγεθος",
"Cover": "Εξώφυλλο",
"Contain": "Περιέχει",
"{{number}} pages left in chapter": "Μένουν {{number}} σελίδες στο κεφάλαιο",
"{{number}} pages left in chapter": "<1>Μένουν </1><0>{{number}}</0><1> σελίδες στο κεφάλαιο</1>",
"Device": "Συσκευή",
"E-Ink Mode": "Λειτουργία E-Ink",
"Highlight Colors": "Χρώματα επισήμανσης",
"Auto Screen Brightness": "Αυτόματη φωτεινότητα οθόνης",
"Pagination": "Σελιδοποίηση",
"Disable Double Tap": "Απενεργοποίηση διπλού ταπ",
"Tap to Paginate": "Ταπ για σελιδοποίηση",
@@ -641,7 +628,6 @@
"Strikethrough": "Διαγραμμένο",
"Squiggly": "Κυματιστό",
"Outline": "Περίγραμμα",
"Save Current Color": "Αποθήκευση τρέχουσας χρώματος",
"Quick Colors": "Γρήγορα χρώματα",
"Highlighter": "Υπογραμμιστής",
"Save Book Cover": "Αποθήκευση εξωφύλλου βιβλίου",
@@ -699,9 +685,7 @@
"Validating...": "Γίνεται έλεγχος...",
"View All": "Προβολή όλων",
"Forward": "Μπροστά",
"OPDS Catalog": "Κατάλογος OPDS",
"Home": "Αρχική",
"Library": "Βιβλιοθήκη",
"{{count}} items_one": "{{count}} στοιχείο",
"{{count}} items_other": "{{count}} στοιχεία",
"Download completed": "Η λήψη ολοκληρώθηκε",
@@ -721,5 +705,712 @@
"Last": "Τελευταίο",
"Cannot Load Page": "Δεν είναι δυνατή η φόρτωση της σελίδας",
"An error occurred": "Προέκυψε σφάλμα",
"Online Library": "Online Βιβλιοθήκη"
"Online Library": "Online Βιβλιοθήκη",
"URL must start with http:// or https://": "Το URL πρέπει να ξεκινά με http:// ή https://",
"Title, Author, Tag, etc...": "Τίτλος, Συγγραφέας, Ετικέτα, κ.λπ...",
"Query": "Ερώτημα",
"Subject": "Θέμα",
"Enter {{terms}}": "Εισαγάγετε {{terms}}",
"No search results found": "Δεν βρέθηκαν αποτελέσματα αναζήτησης",
"Failed to load OPDS feed: {{status}} {{statusText}}": "Αποτυχία φόρτωσης ροής OPDS: {{status}} {{statusText}}",
"Search in {{title}}": "Αναζήτηση στο {{title}}",
"Manage Storage": "Διαχείριση αποθήκευσης",
"Failed to load files": "Αποτυχία φόρτωσης αρχείων",
"Deleted {{count}} file(s)_one": "Διαγράφηκε {{count}} αρχείο",
"Deleted {{count}} file(s)_other": "Διαγράφηκαν {{count}} αρχεία",
"Failed to delete {{count}} file(s)_one": "Αποτυχία διαγραφής {{count}} αρχείου",
"Failed to delete {{count}} file(s)_other": "Αποτυχία διαγραφής {{count}} αρχείων",
"Failed to delete files": "Αποτυχία διαγραφής αρχείων",
"Total Files": "Σύνολο αρχείων",
"Total Size": "Συνολικό μέγεθος",
"Quota": "Όριο",
"Used": "Χρησιμοποιήθηκε",
"Files": "Αρχεία",
"Search files...": "Αναζήτηση αρχείων...",
"Newest First": "Νεότερα πρώτα",
"Oldest First": "Παλαιότερα πρώτα",
"Largest First": "Μεγαλύτερα πρώτα",
"Smallest First": "Μικρότερα πρώτα",
"Name A-Z": "Όνομα AZ",
"Name Z-A": "Όνομα ZA",
"{{count}} selected_one": "{{count}} επιλεγμένο",
"{{count}} selected_other": "{{count}} επιλεγμένα",
"Delete Selected": "Διαγραφή επιλεγμένων",
"Created": "Δημιουργήθηκε",
"No files found": "Δεν βρέθηκαν αρχεία",
"No files uploaded yet": "Δεν έχουν ανέβει αρχεία ακόμη",
"files": "αρχεία",
"Page {{current}} of {{total}}": "Σελίδα {{current}} από {{total}}",
"Are you sure to delete {{count}} selected file(s)?_one": "Σίγουρα θέλετε να διαγράψετε {{count}} επιλεγμένο αρχείο;",
"Are you sure to delete {{count}} selected file(s)?_other": "Σίγουρα θέλετε να διαγράψετε {{count}} επιλεγμένα αρχεία;",
"Cloud Storage Usage": "Χρήση αποθήκευσης cloud",
"Rename Group": "Μετονομασία ομάδας",
"From Directory": "Από κατάλογο",
"Successfully imported {{count}} book(s)_one": "Επιτυχής εισαγωγή 1 βιβλίου",
"Successfully imported {{count}} book(s)_other": "Επιτυχής εισαγωγή {{count}} βιβλίων",
"Count": "Πλήθος",
"Start Page": "Αρχική Σελίδα",
"Search in OPDS Catalog...": "Αναζήτηση στον κατάλογο OPDS...",
"Please log in to use advanced TTS features": "Παρακαλώ συνδεθείτε για να χρησιμοποιήσετε προηγμένες λειτουργίες TTS",
"Word limit of 30 words exceeded.": "Υπέρβαση ορίου 30 λέξεων.",
"Proofread": "Διόρθωση",
"Current selection": "Τρέχουσα επιλογή",
"All occurrences in this book": "Όλες οι εμφανίσεις σε αυτό το βιβλίο",
"All occurrences in your library": "Όλες οι εμφανίσεις στη βιβλιοθήκη σας",
"Selected text:": "Επιλεγμένο κείμενο:",
"Replace with:": "Αντικατάσταση με:",
"Enter text...": "Εισαγωγή κειμένου…",
"Case sensitive:": "Διάκριση πεζών-κεφαλαίων:",
"Scope:": "Εύρος εφαρμογής:",
"Selection": "Επιλογή",
"Library": "Βιβλιοθήκη",
"Yes": "Ναι",
"No": "Όχι",
"Proofread Replacement Rules": "Κανόνες αντικατάστασης διόρθωσης",
"Selected Text Rules": "Κανόνες επιλεγμένου κειμένου",
"No selected text replacement rules": "Δεν υπάρχουν κανόνες αντικατάστασης για το επιλεγμένο κείμενο",
"Book Specific Rules": "Κανόνες για συγκεκριμένο βιβλίο",
"No book-level replacement rules": "Δεν υπάρχουν κανόνες αντικατάστασης σε επίπεδο βιβλίου",
"Disable Quick Action": "Απενεργοποίηση γρήγορης ενέργειας",
"Enable Quick Action on Selection": "Ενεργοποίηση γρήγορης ενέργειας κατά την επιλογή",
"None": "Καμία",
"Annotation Tools": "Εργαλεία σχολιασμού",
"Enable Quick Actions": "Ενεργοποίηση γρήγορων ενεργειών",
"Quick Action": "Γρήγορη ενέργεια",
"Copy to Notebook": "Αντιγραφή στο σημειωματάριο",
"Copy text after selection": "Αντιγραφή κειμένου μετά την επιλογή",
"Highlight text after selection": "Επισήμανση κειμένου μετά την επιλογή",
"Annotate text after selection": "Σχολιασμός κειμένου μετά την επιλογή",
"Search text after selection": "Αναζήτηση κειμένου μετά την επιλογή",
"Look up text in dictionary after selection": "Αναζήτηση κειμένου στο λεξικό μετά την επιλογή",
"Translate text after selection": "Μετάφραση κειμένου μετά την επιλογή",
"Read text aloud after selection": "Ανάγνωση κειμένου μετά την επιλογή",
"Proofread text after selection": "Διόρθωση κειμένου μετά την επιλογή",
"{{activeCount}} active, {{pendingCount}} pending": "{{activeCount}} ενεργές, {{pendingCount}} σε αναμονή",
"{{failedCount}} failed": "{{failedCount}} απέτυχαν",
"Waiting...": "Αναμονή...",
"Failed": "Απέτυχε",
"Completed": "Ολοκληρώθηκε",
"Cancelled": "Ακυρώθηκε",
"Retry": "Επανάληψη",
"Active": "Ενεργές",
"Transfer Queue": "Ουρά μεταφορών",
"Upload All": "Ανέβασμα όλων",
"Download All": "Λήψη όλων",
"Resume Transfers": "Συνέχιση μεταφορών",
"Pause Transfers": "Παύση μεταφορών",
"Pending": "Σε αναμονή",
"No transfers": "Χωρίς μεταφορές",
"Retry All": "Επανάληψη όλων",
"Clear Completed": "Εκκαθάριση ολοκληρωμένων",
"Clear Failed": "Εκκαθάριση αποτυχημένων",
"Upload queued: {{title}}": "Ανέβασμα στην ουρά: {{title}}",
"Download queued: {{title}}": "Λήψη στην ουρά: {{title}}",
"Book not found in library": "Το βιβλίο δεν βρέθηκε στη βιβλιοθήκη",
"Unknown error": "Άγνωστο σφάλμα",
"Please log in to continue": "Παρακαλώ συνδεθείτε για να συνεχίσετε",
"Cloud File Transfers": "Μεταφορές αρχείων στο cloud",
"Show Search Results": "Εμφάνιση αποτελεσμάτων",
"Search results for '{{term}}'": "Αποτελέσματα για '{{term}}'",
"Close Search": "Κλείσιμο αναζήτησης",
"Previous Result": "Προηγούμενο αποτέλεσμα",
"Next Result": "Επόμενο αποτέλεσμα",
"Bookmarks": "Σελιδοδείκτες",
"Annotations": "Σχολιασμοί",
"Show Results": "Εμφάνιση αποτελεσμάτων",
"Clear search": "Εκκαθάριση αναζήτησης",
"Clear search history": "Εκκαθάριση ιστορικού αναζήτησης",
"Tap to Toggle Footer": "Πατήστε για εναλλαγή υποσέλιδου",
"Show Current Time": "Εμφάνιση τρέχουσας ώρας",
"Use 24 Hour Clock": "Χρήση 24ωρης μορφής ώρας",
"Show Current Battery Status": "Εμφάνιση τρέχουσας κατάστασης μπαταρίας",
"Exported successfully": "Εξαγωγή επιτυχής",
"Book exported successfully.": "Το βιβλίο εξήχθη επιτυχώς.",
"Failed to export the book.": "Αποτυχία εξαγωγής του βιβλίου.",
"Export Book": "Εξαγωγή βιβλίου",
"Whole word:": "Ολόκληρη λέξη:",
"Date Published": "Ημερομηνία δημοσίευσης",
"Only for TTS:": "Μόνο για TTS:",
"Uploaded": "Μεταφορτώθηκε",
"Downloaded": "Λήφθηκε",
"Deleted": "Διαγράφηκε",
"Note:": "Σημείωση:",
"Time:": "Ώρα:",
"Format Options": "Επιλογές μορφοποίησης",
"Export Date": "Ημερομηνία εξαγωγής",
"Chapter Titles": "Τίτλοι κεφαλαίων",
"Chapter Separator": "Διαχωριστικό κεφαλαίων",
"Highlights": "Επισημάνσεις",
"Note Date": "Ημερομηνία σημείωσης",
"Advanced": "Για προχωρημένους",
"Hide": "Απόκρυψη",
"Show": "Εμφάνιση",
"Use Custom Template": "Χρήση προσαρμοσμένου προτύπου",
"Export Template": "Πρότυπο εξαγωγής",
"Template Syntax:": "Σύνταξη προτύπου:",
"Insert value": "Εισαγωγή τιμής",
"Format date (locale)": "Μορφοποίηση ημερομηνίας (τοπική)",
"Format date (custom)": "Μορφοποίηση ημερομηνίας (προσαρμοσμένη)",
"Conditional": "Υπό όρους",
"Loop": "Βρόχος",
"Available Variables:": "Διαθέσιμες μεταβλητές:",
"Book title": "Τίτλος βιβλίου",
"Book author": "Συγγραφέας βιβλίου",
"Export date": "Ημερομηνία εξαγωγής",
"Array of chapters": "Πίνακας κεφαλαίων",
"Chapter title": "Τίτλος κεφαλαίου",
"Array of annotations": "Πίνακας σχολιασμών",
"Highlighted text": "Επισημασμένο κείμενο",
"Annotation note": "Σημείωση σχολιασμού",
"Date Format Tokens:": "Σύμβολα μορφοποίησης ημερομηνίας:",
"Year (4 digits)": "Έτος (4 ψηφία)",
"Month (01-12)": "Μήνας (01-12)",
"Day (01-31)": "Ημέρα (01-31)",
"Hour (00-23)": "Ώρα (00-23)",
"Minute (00-59)": "Λεπτό (00-59)",
"Second (00-59)": "Δευτερόλεπτο (00-59)",
"Show Source": "Εμφάνιση πηγής",
"No content to preview": "Δεν υπάρχει περιεχόμενο για προεπισκόπηση",
"Export": "Εξαγωγή",
"Set Timeout": "Ορισμός χρονικού ορίου",
"Select Voice": "Επιλογή φωνής",
"Toggle Sticky Bottom TTS Bar": "Εναλλαγή καρφιτσωμένης μπάρας TTS",
"Display what I'm reading on Discord": "Εμφάνιση του βιβλίου που διαβάζω στο Discord",
"Show on Discord": "Εμφάνιση στο Discord",
"Instant {{action}}": "Άμεση {{action}}",
"Instant {{action}} Disabled": "Άμεση {{action}} απενεργοποιημένη",
"Annotation": "Σημείωση",
"Reset Template": "Επαναφορά προτύπου",
"Annotation style": "Στυλ σχολίου",
"Annotation color": "Χρώμα σχολίου",
"Annotation time": "Ώρα σχολίου",
"AI": "AI",
"Are you sure you want to re-index this book?": "Είστε σίγουροι ότι θέλετε να επαναδημιουργήσετε το ευρετήριο αυτού του βιβλίου;",
"Enable AI in Settings": "Ενεργοποίηση AI στις ρυθμίσεις",
"Index This Book": "Δημιουργία ευρετηρίου για αυτό το βιβλίο",
"Enable AI search and chat for this book": "Ενεργοποίηση αναζήτησης AI και συνομιλίας για αυτό το βιβλίο",
"Start Indexing": "Έναρξη δημιουργίας ευρετηρίου",
"Indexing book...": "Δημιουργία ευρετηρίου βιβλίου...",
"Preparing...": "Προετοιμασία...",
"Delete this conversation?": "Διαγραφή αυτής της συνομιλίας;",
"No conversations yet": "Δεν υπάρχουν συνομιλίες ακόμα",
"Start a new chat to ask questions about this book": "Ξεκινήστε μια νέα συνομιλία για να κάνετε ερωτήσεις σχετικά με αυτό το βιβλίο",
"Rename": "Μετονομασία",
"New Chat": "Νέα συνομιλία",
"Chat": "Συνομιλία",
"Please enter a model ID": "Εισάγετε ένα αναγνωριστικό μοντέλου",
"Model not available or invalid": "Το μοντέλο δεν είναι διαθέσιμο ή δεν είναι έγκυρο",
"Failed to validate model": "Αποτυχία επικύρωσης μοντέλου",
"Couldn't connect to Ollama. Is it running?": "Δεν ήταν δυνατή η σύνδεση με το Ollama. Εκτελείται;",
"Invalid API key or connection failed": "Μη έγκυρο κλειδί API ή αποτυχία σύνδεσης",
"Connection failed": "Αποτυχία σύνδεσης",
"AI Assistant": "Βοηθός AI",
"Enable AI Assistant": "Ενεργοποίηση βοηθού AI",
"Provider": "Πάροχος",
"Ollama (Local)": "Ollama (Τοπικό)",
"AI Gateway (Cloud)": "Πύλη AI (Cloud)",
"Ollama Configuration": "Διαμόρφωση Ollama",
"Refresh Models": "Ανανέωση μοντέλων",
"AI Model": "Μοντέλο AI",
"No models detected": "Δεν εντοπίστηκαν μοντέλα",
"AI Gateway Configuration": "Διαμόρφωση πύλης AI",
"Choose from a selection of high-quality, economical AI models. You can also bring your own model by selecting \"Custom Model\" below.": "Επιλέξτε από μια συλλογή υψηλής ποιότητας, οικονομικών μοντέλων AI. Μπορείτε επίσης να χρησιμοποιήσετε το δικό σας μοντέλο επιλέγοντας \"Προσαρμοσμένο μοντέλο\" παρακάτω.",
"API Key": "Κλειδί API",
"Get Key": "Λήψη κλειδιού",
"Model": "Μοντέλο",
"Custom Model...": "Προσαρμοσμένο μοντέλο...",
"Custom Model ID": "Αναγνωριστικό προσαρμοσμένου μοντέλου",
"Validate": "Επικύρωση",
"Model available": "Το μοντέλο είναι διαθέσιμο",
"Connection": "Σύνδεση",
"Test Connection": "Δοκιμή σύνδεσης",
"Connected": "Συνδεδεμένο",
"Custom Colors": "Προσαρμοσμένα χρώματα",
"Color E-Ink Mode": "Λειτουργία έγχρωμου E-Ink",
"Reading Ruler": "Χάρακας ανάγνωσης",
"Enable Reading Ruler": "Ενεργοποίηση χάρακα ανάγνωσης",
"Lines to Highlight": "Γραμμές για επισήμανση",
"Ruler Color": "Χρώμα χάρακα",
"Command Palette": "Παλέτα εντολών",
"Search settings and actions...": "Αναζήτηση ρυθμίσεων και ενεργειών...",
"No results found for": "Δεν βρέθηκαν αποτελέσματα για",
"Type to search settings and actions": "Πληκτρολογήστε για αναζήτηση ρυθμίσεων και ενεργειών",
"Recent": "Πρόσφατα",
"navigate": "πλοήγηση",
"select": "επιλογή",
"close": "κλείσιμο",
"Search Settings": "Αναζήτηση ρυθμίσεων",
"Page Margins": "Περιθώρια σελίδας",
"AI Provider": "Πάροχος AI",
"Ollama URL": "URL Ollama",
"Ollama Model": "Μοντέλο Ollama",
"AI Gateway Model": "Μοντέλο AI Gateway",
"Actions": "Ενέργειες",
"Navigation": "Πλοήγηση",
"Set status for {{count}} book(s)_one": "Ορισμός κατάστασης για {{count}} βιβλίο",
"Set status for {{count}} book(s)_other": "Ορισμός κατάστασης για {{count}} βιβλία",
"Mark as Unread": "Σήμανση ως μη αναγνωσμένο",
"Mark as Finished": "Σήμανση ως ολοκληρωμένο",
"Finished": "Ολοκληρώθηκε",
"Unread": "Μη αναγνωσμένο",
"Clear Status": "Εκκαθάριση κατάστασης",
"Status": "Κατάσταση",
"Loading": "Φόρτωση...",
"Exit Paragraph Mode": "Έξοδος από τη λειτουργία παραγράφου",
"Paragraph Mode": "Λειτουργία παραγράφου",
"Embedding Model": "Μοντέλο ενσωμάτωσης",
"{{count}} book(s) synced_one": "{{count}} βιβλίο συγχρονίστηκε",
"{{count}} book(s) synced_other": "{{count}} βιβλία συγχρονίστηκαν",
"Unable to start RSVP": "Αδυναμία έναρξης RSVP",
"RSVP not supported for PDF": "Το RSVP δεν υποστηρίζεται για PDF",
"Select Chapter": "Επιλογή Κεφαλαίου",
"Context": "Πλαίσιο",
"Ready": "Έτοιμο",
"Chapter Progress": "Πρόοδος Κεφαλαίου",
"words": "λέξεις",
"{{time}} left": "{{time}} απομένουν",
"Reading progress": "Πρόοδος ανάγνωσης",
"Skip back 15 words": "Επιστροφή 15 λέξεων",
"Back 15 words (Shift+Left)": "Πίσω 15 λέξεις (Shift+Αριστερά)",
"Pause (Space)": "Παύση (Διάστημα)",
"Play (Space)": "Αναπαραγωγή (Διάστημα)",
"Skip forward 15 words": "Προώθηση 15 λέξεων",
"Forward 15 words (Shift+Right)": "Εμπρός 15 λέξεις (Shift+Δεξιά)",
"Decrease speed": "Μείωση ταχύτητας",
"Slower (Left/Down)": "Πιο αργά (Αριστερά/Κάτω)",
"Increase speed": "Αύξηση ταχύτητας",
"Faster (Right/Up)": "Πιο γρήγορα (Δεξιά/Πάνω)",
"Start RSVP Reading": "Έναρξη ανάγνωσης RSVP",
"Choose where to start reading": "Επιλέξτε από πού θα ξεκινήσετε την ανάγνωση",
"From Chapter Start": "Από την αρχή του κεφαλαίου",
"Start reading from the beginning of the chapter": "Ξεκινήστε την ανάγνωση από την αρχή του κεφαλαίου",
"Resume": "Συνέχεια",
"Continue from where you left off": "Συνεχίστε από εκεί που σταματήσατε",
"From Current Page": "Από την τρέχουσα σελίδα",
"Start from where you are currently reading": "Ξεκινήστε από εκεί που διαβάζετε αυτή τη στιγμή",
"From Selection": "Από την επιλογή",
"Speed Reading Mode": "Λειτουργία ταχείας ανάγνωσης",
"Scroll left": "Κύλιση αριστερά",
"Scroll right": "Κύλιση δεξιά",
"Library Sync Progress": "Πρόοδος συγχρονισμού βιβλιοθήκης",
"Back to library": "Επιστροφή στη βιβλιοθήκη",
"Group by...": "Ομαδοποίηση κατά...",
"Export as Plain Text": "Αποστολή ως απλό κείμενο",
"Export as Markdown": "Αποστολή ως Markdown",
"Show Page Navigation Buttons": "Κουμπιά πλοήγησης",
"Page {{number}}": "Σελίδα {{number}}",
"highlight": "επισήμανση",
"underline": "υπογράμμιση",
"squiggly": "κυματιστή γραμμή",
"red": "κόκκινο",
"violet": "βιολετί",
"blue": "μπλε",
"green": "πράσινο",
"yellow": "κίτρινο",
"Select {{style}} style": "Επιλέξτε στυλ {{style}}",
"Select {{color}} color": "Επιλέξτε χρώμα {{color}}",
"Close Book": "Κλείσιμο βιβλίου",
"Speed Reading": "Γρήγορη Ανάγνωση",
"Close Speed Reading": "Κλείσιμο Γρήγορης Ανάγνωσης",
"Authors": "Συγγραφείς",
"Books": "Βιβλία",
"Groups": "Ομάδες",
"Back to TTS Location": "Επιστροφή στην τοποθεσία TTS",
"Metadata": "Μεταδεδομένα",
"Image viewer": "Πρόγραμμα προβολής εικόνων",
"Previous Image": "Προηγούμενη εικόνα",
"Next Image": "Επόμενη εικόνα",
"Zoomed": "Ζουμαρισμένο",
"Zoom level": "Επίπεδο ζουμ",
"Table viewer": "Πρόγραμμα προβολής πινάκων",
"Unable to connect to Readwise. Please check your network connection.": "Αδυναμία σύνδεσης στο Readwise. Ελέγξτε τη σύνδεση του δικτύου σας.",
"Invalid Readwise access token": "Μη έγκυρο διακριτικό πρόσβασης Readwise",
"Disconnected from Readwise": "Αποσυνδέθηκε από το Readwise",
"Never": "Ποτέ",
"Sync Enabled": "Ο συγχρονισμός ενεργοποιήθηκε",
"Disconnect": "Αποσύνδεση",
"Connect your Readwise account to sync highlights.": "Συνδέστε τον λογαριασμό σας Readwise για να συγχρονίσετε τις επισημάνσεις.",
"Get your access token at": "Αποκτήστε το διακριτικό πρόσβασής σας στο",
"Access Token": "Διακριτικό πρόσβασης",
"Paste your Readwise access token": "Επικολλήστε το διακριτικό πρόσβασης Readwise",
"Readwise Sync": "Συγχρονισμός Readwise",
"Push Highlights": "Προώθηση επισημάνσεων",
"Highlights synced to Readwise": "Οι επισημάνσεις συγχρονίστηκαν στο Readwise",
"Readwise sync failed: no internet connection": "Ο συγχρονισμός Readwise απέτυχε: δεν υπάρχει σύνδεση στο διαδίκτυο",
"Readwise sync failed: {{error}}": "Ο συγχρονισμός Readwise απέτυχε: {{error}}",
"System Screen Brightness": "Φωτεινότητα οθόνης συστήματος",
"Page:": "Σελίδα:",
"Page: {{number}}": "Σελίδα: {{number}}",
"Annotation page number": "Αριθμός σελίδας σχολίου",
"Translating...": "Μετάφραση...",
"Show Battery Percentage": "Εμφάνιση ποσοστού μπαταρίας",
"Hide Scrollbar": "Απόκρυψη γραμμής κύλισης",
"Skip to last reading position": "Μετάβαση στην τελευταία θέση ανάγνωσης",
"Show context": "Εμφάνιση πλαισίου",
"Hide context": "Απόκρυψη πλαισίου",
"Decrease font size": "Μείωση μεγέθους γραμματοσειράς",
"Increase font size": "Αύξηση μεγέθους γραμματοσειράς",
"Focus": "Εστίαση",
"Theme color": "Χρώμα θέματος",
"Import failed": "Η εισαγωγή απέτυχε",
"Available Formatters:": "Διαθέσιμοι μορφοποιητές:",
"Format date": "Μορφοποίηση ημερομηνίας",
"Markdown block quote (> per line)": "Παράθεση Markdown (> ανά γραμμή)",
"Newlines to <br>": "Αλλαγές γραμμής σε <br>",
"Change case": "Αλλαγή πεζών/κεφαλαίων",
"Trim whitespace": "Αφαίρεση κενών",
"Truncate to n characters": "Περικοπή σε n χαρακτήρες",
"Replace text": "Αντικατάσταση κειμένου",
"Fallback value": "Εναλλακτική τιμή",
"Get length": "Λήψη μήκους",
"First/last element": "Πρώτο/τελευταίο στοιχείο",
"Join array": "Ένωση πίνακα",
"Backup failed: {{error}}": "Η δημιουργία αντιγράφου ασφαλείας απέτυχε: {{error}}",
"Select Backup": "Επιλογή αντιγράφου ασφαλείας",
"Restore failed: {{error}}": "Η επαναφορά απέτυχε: {{error}}",
"Backup & Restore": "Αντίγραφα ασφαλείας & Επαναφορά",
"Create a backup of your library or restore from a previous backup. Restoring will merge with your current library.": "Δημιουργήστε αντίγραφο ασφαλείας της βιβλιοθήκης σας ή επαναφέρετε από προηγούμενο αντίγραφο. Η επαναφορά θα συγχωνευθεί με την τρέχουσα βιβλιοθήκη σας.",
"Backup Library": "Αντίγραφο βιβλιοθήκης",
"Restore Library": "Επαναφορά βιβλιοθήκης",
"Creating backup...": "Δημιουργία αντιγράφου ασφαλείας...",
"Restoring library...": "Επαναφορά βιβλιοθήκης...",
"{{current}} of {{total}} items": "{{current}} από {{total}} στοιχεία",
"Backup completed successfully!": "Το αντίγραφο ασφαλείας ολοκληρώθηκε!",
"Restore completed successfully!": "Η επαναφορά ολοκληρώθηκε!",
"Your library has been saved to the selected location.": "Η βιβλιοθήκη σας αποθηκεύτηκε στην επιλεγμένη τοποθεσία.",
"{{added}} books added, {{updated}} books updated.": "{{added}} βιβλία προστέθηκαν, {{updated}} βιβλία ενημερώθηκαν.",
"Operation failed": "Η λειτουργία απέτυχε",
"Loading library...": "Φόρτωση βιβλιοθήκης...",
"{{count}} books refreshed_one": "{{count}} βιβλίο ανανεώθηκε",
"{{count}} books refreshed_other": "{{count}} βιβλία ανανεώθηκαν",
"Failed to refresh metadata": "Αποτυχία ανανέωσης μεταδεδομένων",
"Refresh Metadata": "Ανανέωση μεταδεδομένων",
"Keyboard Shortcuts": "Συντομεύσεις πληκτρολογίου",
"View all keyboard shortcuts": "Προβολή όλων των συντομεύσεων",
"Switch Sidebar Tab": "Εναλλαγή καρτέλας πλαϊνής γραμμής",
"Toggle Notebook": "Εμφάνιση/Απόκρυψη σημειωματάριου",
"Search in Book": "Αναζήτηση στο βιβλίο",
"Toggle Scroll Mode": "Εναλλαγή λειτουργίας κύλισης",
"Toggle Select Mode": "Εναλλαγή λειτουργίας επιλογής",
"Toggle Bookmark": "Εναλλαγή σελιδοδείκτη",
"Toggle Text to Speech": "Εναλλαγή κειμένου σε ομιλία",
"Play / Pause TTS": "Αναπαραγωγή / Παύση TTS",
"Toggle Paragraph Mode": "Εναλλαγή λειτουργίας παραγράφου",
"Highlight Selection": "Επισήμανση επιλογής",
"Underline Selection": "Υπογράμμιση επιλογής",
"Annotate Selection": "Σχολιασμός επιλογής",
"Search Selection": "Αναζήτηση επιλογής",
"Copy Selection": "Αντιγραφή επιλογής",
"Translate Selection": "Μετάφραση επιλογής",
"Dictionary Lookup": "Αναζήτηση στο λεξικό",
"Read Aloud Selection": "Ανάγνωση επιλογής",
"Proofread Selection": "Διόρθωση επιλογής",
"Open Settings": "Άνοιγμα ρυθμίσεων",
"Open Command Palette": "Άνοιγμα παλέτας εντολών",
"Show Keyboard Shortcuts": "Εμφάνιση συντομεύσεων",
"Open Books": "Άνοιγμα βιβλίων",
"Toggle Fullscreen": "Εναλλαγή πλήρους οθόνης",
"Close Window": "Κλείσιμο παραθύρου",
"Quit App": "Έξοδος εφαρμογής",
"Go Left / Previous Page": "Αριστερά / Προηγούμενη σελίδα",
"Go Right / Next Page": "Δεξιά / Επόμενη σελίδα",
"Go Up": "Πάνω",
"Go Down": "Κάτω",
"Previous Chapter": "Προηγούμενο κεφάλαιο",
"Next Chapter": "Επόμενο κεφάλαιο",
"Scroll Half Page Down": "Κύλιση μισής σελίδας κάτω",
"Scroll Half Page Up": "Κύλιση μισής σελίδας πάνω",
"Save Note": "Αποθήκευση σημείωσης",
"Single Section Scroll": "Κύλιση μεμονωμένης ενότητας",
"General": "Γενικά",
"Text to Speech": "Κείμενο σε ομιλία",
"Zoom": "Ζουμ",
"Window": "Παράθυρο",
"Your Bookshelf": "Η βιβλιοθήκη σου",
"TTS": "Εκφώνηση",
"Media Info": "Πληροφορίες μέσων",
"Update Frequency": "Συχνότητα ενημέρωσης",
"Every Sentence": "Κάθε πρόταση",
"Every Paragraph": "Κάθε παράγραφο",
"Every Chapter": "Κάθε κεφάλαιο",
"TTS Media Info Update Frequency": "Συχνότητα ενημέρωσης πληροφοριών μέσων TTS",
"Select reading speed": "Επιλέξτε ταχύτητα ανάγνωσης",
"Drag to seek": "Σύρετε για αναζήτηση",
"Punctuation Delay": "Καθυστέρηση στίξης",
"Please confirm that this OPDS connection will be proxied through Readest servers on the web app before continuing.": "Επιβεβαιώστε ότι αυτή η σύνδεση OPDS θα δρομολογηθεί μέσω των διακομιστών Readest στην εφαρμογή ιστού πριν συνεχίσετε.",
"Custom Headers (optional)": "Προσαρμοσμένες κεφαλίδες (προαιρετικό)",
"Add one header per line using \"Header-Name: value\".": "Προσθέστε μία κεφαλίδα ανά γραμμή χρησιμοποιώντας \"Header-Name: value\".",
"I understand this OPDS connection will be proxied through Readest servers on the web app. If I do not trust Readest with these credentials or headers, I should use the native app instead.": "Κατανοώ ότι αυτή η σύνδεση OPDS θα δρομολογηθεί μέσω των διακομιστών Readest στην εφαρμογή ιστού. Αν δεν εμπιστεύομαι τo Readest με αυτά τα διαπιστευτήρια ή τις κεφαλίδες, θα πρέπει να χρησιμοποιήσω την εγγενή εφαρμογή.",
"Unable to connect to Hardcover. Please check your network connection.": "Αδυναμία σύνδεσης στο Hardcover. Ελέγξτε τη σύνδεση δικτύου σας.",
"Invalid Hardcover API token": "Μη έγκυρο Hardcover API token",
"Disconnected from Hardcover": "Αποσυνδέθηκε από το Hardcover",
"Connect your Hardcover account to sync reading progress and notes.": "Συνδέστε τον λογαριασμό σας Hardcover για συγχρονισμό προόδου ανάγνωσης και σημειώσεων.",
"Get your API token from hardcover.app → Settings → API.": "Λάβετε το API token σας από hardcover.app → Ρυθμίσεις → API.",
"API Token": "API Token",
"Paste your Hardcover API token": "Επικολλήστε το Hardcover API token σας",
"Hardcover Sync": "Συγχρονισμός Hardcover",
"Push Notes": "Αποστολή σημειώσεων",
"No annotations or excerpts to sync for this book.": "Δεν υπάρχουν σχολιασμοί ή αποσπάσματα για συγχρονισμό σε αυτό το βιβλίο.",
"Configure Hardcover in Settings first.": "Ρυθμίστε πρώτα το Hardcover στις Ρυθμίσεις.",
"No new Hardcover note changes to sync.": "Δεν υπάρχουν νέες αλλαγές σημειώσεων Hardcover για συγχρονισμό.",
"Hardcover synced: {{inserted}} new, {{updated}} updated, {{skipped}} unchanged": "Hardcover συγχρονίστηκε: {{inserted}} νέα, {{updated}} ενημερωμένα, {{skipped}} αμετάβλητα",
"Hardcover notes sync failed: {{error}}": "Αποτυχία συγχρονισμού σημειώσεων Hardcover: {{error}}",
"Reading progress synced to Hardcover": "Η πρόοδος ανάγνωσης συγχρονίστηκε στο Hardcover",
"Hardcover progress sync failed: {{error}}": "Αποτυχία συγχρονισμού προόδου Hardcover: {{error}}",
"ISBN": "ISBN",
"Keep existing source identifier": "Διατήρηση υπάρχοντος αναγνωριστικού πηγής",
"Toggle Toolbar": "Εναλλαγή γραμμής εργαλείων",
"Split Hyphens": "Διαχωρισμός παύλων",
"Apply Theme Colors to PDF": "Εφαρμογή χρωμάτων θέματος σε PDF",
"Name": "Όνομα",
"Remove": "Αφαίρεση",
"Edit OPDS Catalog": "Επεξεργασία καταλόγου OPDS",
"Save Changes": "Αποθήκευση αλλαγών",
"Use Book Layout": "Χρήση διάταξης βιβλίου",
"End of this section. Continue to the next.": "Τέλος αυτής της ενότητας. Συνεχίστε στην επόμενη.",
"Auto-download": "Αυτόματη λήψη",
"Auto-download new items": "Αυτόματη λήψη νέων στοιχείων",
"Automatically download new publications when the app syncs": "Αυτόματη λήψη νέων δημοσιεύσεων κατά τον συγχρονισμό της εφαρμογής",
"{{count}} new item(s) downloaded from OPDS_one": "Λήφθηκε {{count}} νέο στοιχείο από OPDS",
"{{count}} new item(s) downloaded from OPDS_other": "Λήφθηκαν {{count}} νέα στοιχεία από OPDS",
"Failed to sync {{count}} OPDS catalog(s)_one": "Αποτυχία συγχρονισμού {{count}} καταλόγου OPDS",
"Failed to sync {{count}} OPDS catalog(s)_other": "Αποτυχία συγχρονισμού {{count}} καταλόγων OPDS",
"Last synced {{when}}": "Τελευταίος συγχρονισμός {{when}}",
"Failed downloads": "Αποτυχημένες λήψεις",
"No failed downloads": "Δεν υπάρχουν αποτυχημένες λήψεις",
"Attempts: {{count}}_one": "Προσπάθειες: {{count}}",
"Attempts: {{count}}_other": "Προσπάθειες: {{count}}",
"Skip": "Παράλειψη",
"Skip all": "Παράλειψη όλων",
"Retry all": "Επανάληψη όλων",
"{{count}} failed_one": "{{count}} απέτυχε",
"{{count}} failed_other": "{{count}} απέτυχαν",
"(none)": "(κανένα)",
"Identifiers": "Αναγνωριστικά",
"Read (Stream)": "Ανάγνωση (Ροή)",
"Failed to start stream": "Αποτυχία έναρξης ροής",
"No dictionaries enabled": "Δεν είναι ενεργοποιημένο κανένα λεξικό",
"Enable a dictionary in Settings → Language → Dictionaries.": "Ενεργοποιήστε ένα λεξικό από Ρυθμίσεις → Γλώσσα → Λεξικά.",
"Wiktionary": "Wiktionary",
"Drag to reorder": "Σύρετε για επαναταξινόμηση",
"Built-in": "Ενσωματωμένο",
"Bundle is missing on this device. Re-import to use it.": "Το πακέτο λείπει από αυτή τη συσκευή. Επανεισαγάγετέ το για χρήση.",
"This dictionary format is not supported.": "Αυτή η μορφή λεξικού δεν υποστηρίζεται.",
"MDict": "MDict",
"StarDict": "StarDict",
"Imported {{count}} dictionary_one": "Εισήχθη {{count}} λεξικό",
"Imported {{count}} dictionary_other": "Εισήχθησαν {{count}} λεξικά",
"Skipped incomplete bundles: {{names}}": "Παραλείφθηκαν ημιτελή πακέτα: {{names}}",
"Failed to import dictionary: {{message}}": "Αποτυχία εισαγωγής λεξικού: {{message}}",
"Dictionaries": "Λεξικά",
"Delete Dictionary": "Διαγραφή λεξικού",
"Importing…": "Εισαγωγή…",
"Import Dictionary": "Εισαγωγή λεξικού",
"No dictionaries available.": "Δεν υπάρχουν διαθέσιμα λεξικά.",
"StarDict bundles need .ifo, .idx, and .dict.dz files (.syn optional).": "Τα πακέτα StarDict χρειάζονται αρχεία .ifo, .idx και .dict.dz (το .syn είναι προαιρετικό).",
"Select all the bundle files together when importing.": "Επιλέξτε όλα τα αρχεία του πακέτου μαζί κατά την εισαγωγή.",
"Manage Dictionaries": "Διαχείριση λεξικών",
"Select Dictionary Files": "Επιλογή αρχείων λεξικού",
"Read on Wikipedia →": "Διάβασέ το στη Wikipedia →",
"This link can't be opened": "Δεν είναι δυνατό το άνοιγμα αυτού του συνδέσμου",
"The annotation link is missing required information. The original link may have been truncated.": "Από τον σύνδεσμο σχολιασμού λείπουν οι απαιτούμενες πληροφορίες. Ο αρχικός σύνδεσμος ενδέχεται να έχει αποκοπεί.",
"Go to Readest": "Μετάβαση στο Readest",
"Open-source ebook reader for everyone, on every device.": "Αναγνώστης ηλεκτρονικών βιβλίων ανοικτού κώδικα για όλους, σε κάθε συσκευή.",
"Open in Readest": "Άνοιγμα στο Readest",
"If Readest didn't open automatically, choose an option below:": "Αν το Readest δεν άνοιξε αυτόματα, επιλέξτε μία από τις παρακάτω επιλογές:",
"Continue reading where you left off.": "Συνεχίστε να διαβάζετε από εκεί που σταματήσατε.",
"Readest logo": "Λογότυπο Readest",
"Opening Readest...": "Γίνεται άνοιγμα του Readest…",
"Open in Readest app": "Άνοιγμα στην εφαρμογή Readest",
"Continue in browser": "Συνέχεια στο πρόγραμμα περιήγησης",
"Don't have Readest?": "Δεν έχετε το Readest;",
"Book not in your library": "Το βιβλίο δεν βρίσκεται στη βιβλιοθήκη σας",
"Share Book": "Κοινή χρήση βιβλίου",
"Could not create share link": "Δεν ήταν δυνατή η δημιουργία συνδέσμου κοινής χρήσης",
"Link copied": "Ο σύνδεσμος αντιγράφηκε",
"Could not copy link": "Δεν ήταν δυνατή η αντιγραφή του συνδέσμου",
"Share revoked": "Η κοινή χρήση ανακλήθηκε",
"Could not revoke share": "Δεν ήταν δυνατή η ανάκληση της κοινής χρήσης",
"Uploading book…": "Μεταφόρτωση βιβλίου…",
"Generating…": "Δημιουργία…",
"Generate share link": "Δημιουργία συνδέσμου κοινής χρήσης",
"Share URL": "URL κοινής χρήσης",
"Copy link": "Αντιγραφή συνδέσμου",
"Copied": "Αντιγράφηκε",
"Share via…": "Κοινή χρήση μέσω…",
"Expires {{date}}": "Λήγει στις {{date}}",
"Revoking…": "Ανάκληση…",
"Revoke share": "Ανάκληση κοινής χρήσης",
"Sign in to share books": "Συνδεθείτε για να μοιραστείτε βιβλία",
"Expiring soon": "Λήγει σύντομα",
"Missing share token": "Λείπει το διακριτικό κοινής χρήσης",
"Could not add to your library": "Δεν ήταν δυνατή η προσθήκη στη βιβλιοθήκη σας",
"This share link is no longer available": "Αυτός ο σύνδεσμος κοινής χρήσης δεν είναι πλέον διαθέσιμος",
"The original link may have expired or been revoked.": "Ο αρχικός σύνδεσμος ενδέχεται να έχει λήξει ή να έχει ανακληθεί.",
"Get Readest": "Αποκτήστε το Readest",
"Loading shared book…": "Φόρτωση κοινόχρηστου βιβλίου…",
"Adding…": "Προσθήκη…",
"Add to my library": "Προσθήκη στη βιβλιοθήκη μου",
"Could not load your shares": "Δεν ήταν δυνατή η φόρτωση των κοινοποιήσεών σας",
"Revoked": "Ανακλήθηκε",
"Expired": "Έληξε",
"Shared books": "Κοινόχρηστα βιβλία",
"You haven't shared any books yet": "Δεν έχετε μοιραστεί κανένα βιβλίο ακόμη",
"Open a book and tap Share to send it to a friend.": "Ανοίξτε ένα βιβλίο και πατήστε «Κοινή χρήση» για να το στείλετε σε έναν φίλο.",
"{{count}} active_one": "{{count}} ενεργό",
"{{count}} active_other": "{{count}} ενεργά",
"{{count}} downloads_one": "{{count}} λήψη",
"{{count}} downloads_other": "{{count}} λήψεις",
"starts at saved page": "ξεκινά από την αποθηκευμένη σελίδα",
"More actions": "Περισσότερες ενέργειες",
"Loading…": "Φόρτωση…",
"Load more": "Φόρτωση περισσότερων",
"Could not load shared book": "Δεν ήταν δυνατή η φόρτωση του κοινόχρηστου βιβλίου",
"The share link is missing required information.": "Από τον σύνδεσμο κοινής χρήσης λείπουν απαιτούμενες πληροφορίες.",
"Please check your connection and try again.": "Ελέγξτε τη σύνδεσή σας και δοκιμάστε ξανά.",
"Sign in to import shared books": "Συνδεθείτε για να εισαγάγετε κοινόχρηστα βιβλία",
"Already in your library": "Υπάρχει ήδη στη βιβλιοθήκη σας",
"Added to your library": "Προστέθηκε στη βιβλιοθήκη σας",
"Could not import shared book": "Δεν ήταν δυνατή η εισαγωγή του κοινόχρηστου βιβλίου",
"Manage Shared Links": "Διαχείριση συνδέσμων κοινής χρήσης",
"Expires in": "Λήγει σε",
"{{count}} days_one": "1 ημέρα",
"{{count}} days_other": "{{count}} ημέρες",
"Expires in {{count}} days_one": "Λήγει σε 1 ημέρα",
"Expires in {{count}} days_other": "Λήγει σε {{count}} ημέρες",
"Expires in {{count}} hours_one": "Λήγει σε 1 ώρα",
"Expires in {{count}} hours_other": "Λήγει σε {{count}} ώρες",
"Open in app": "Άνοιγμα στην εφαρμογή",
"Shared with you": "Κοινοποιήθηκε σε εσάς",
"Downloading… {{percent}}%": "Λήψη… {{percent}}%",
"Import progress": "Πρόοδος εισαγωγής",
"Share reading progress": "Κοινή χρήση προόδου ανάγνωσης",
"Includes your reading progress": "Περιλαμβάνει την πρόοδο ανάγνωσής σας",
"Synced {{time}}": "Συγχρονίστηκε {{time}}",
"Sync Info": "Πληροφορίες συγχρονισμού",
"Last Synced": "Τελευταίος συγχρονισμός",
"DICT": "DICT",
"Slob": "Slob",
"DICT bundles need a .index file and a .dict.dz file.": "Τα πακέτα DICT χρειάζονται ένα αρχείο .index και ένα αρχείο .dict.dz.",
"Slob bundles need a .slob file.": "Τα πακέτα Slob χρειάζονται ένα αρχείο .slob.",
"URL template must start with http(s):// and contain %WORD%.": "Το πρότυπο URL πρέπει να ξεκινά με http(s):// και να περιέχει %WORD%.",
"Web": "Διαδίκτυο",
"Add Web Search": "Προσθήκη αναζήτησης ιστού",
"Edit Web Search": "Επεξεργασία αναζήτησης ιστού",
"e.g. Google": "π.χ. Google",
"URL Template": "Πρότυπο URL",
"Use %WORD% where the looked-up word should appear.": "Χρησιμοποιήστε %WORD% όπου πρέπει να εμφανίζεται η λέξη αναζήτησης.",
"Open the search result in your browser:": "Ανοίξτε το αποτέλεσμα αναζήτησης στο πρόγραμμα περιήγησής σας:",
"Open in {{name}}": "Άνοιγμα σε {{name}}",
"Book Fingerprint": "Δακτυλικό αποτύπωμα βιβλίου",
"Search the web": "Αναζήτηση στον ιστό",
"App deeplink (readest://)": "Σύνδεσμος εφαρμογής (readest://)",
"Universal web link (https://)": "Γενικός σύνδεσμος ιστού (https://)",
"Name cannot be empty.": "Το όνομα δεν μπορεί να είναι κενό.",
"Replaced {{count}} existing dictionary_one": "Αντικαταστάθηκε {{count}} υπάρχον λεξικό",
"Replaced {{count}} existing dictionary_other": "Αντικαταστάθηκαν {{count}} υπάρχοντα λεξικά",
"Cancel Edit": "Ακύρωση επεξεργασίας",
"Edit Dictionary": "Επεξεργασία λεξικού",
"MDict bundles use .mdx files; companion .mdd and .css files are optional.": "Τα πακέτα MDict χρησιμοποιούν αρχεία .mdx· τα συνοδευτικά αρχεία .mdd και .css είναι προαιρετικά.",
"Dictionary name": "Όνομα λεξικού",
"This audio can't play here — the dictionary uses an outdated format. Try one with Opus, MP3, or WAV audio.": "Αυτός ο ήχος δεν μπορεί να αναπαραχθεί εδώ — το λεξικό χρησιμοποιεί ξεπερασμένη μορφή. Δοκιμάστε ένα λεξικό με ήχο Opus, MP3 ή WAV.",
"File uploaded: {{title}}": "Το αρχείο με τίτλο {{title}} ανέβηκε",
"File downloaded: {{title}}": "Το αρχείο με τίτλο {{title}} κατέβηκε",
"Deleted cloud copy of the file: {{title}}": "Διαγράφηκε το αντίγραφο του αρχείου: {{title}}",
"Failed to upload file: {{title}}": "Αποτυχία ανέβασμα αρχείου: {{title}}",
"Failed to download file: {{title}}": "Αποτυχία λήψης αρχείου: {{title}}",
"Failed to delete cloud copy of the file: {{title}}": "Αποτυχία διαγραφής του αντιγράφου του αρχείου: {{title}}",
"{{percentage}}% used": "{{percentage}}% σε χρήση",
"Set PIN…": "Ορισμός PIN…",
"Require a 4-digit PIN to open Readest": "Απαιτείται 4ψήφιο PIN για το άνοιγμα του Readest",
"Change PIN…": "Αλλαγή PIN…",
"Disable PIN…": "Απενεργοποίηση PIN…",
"Sync passphrase ready": "Η φράση πρόσβασης συγχρονισμού είναι έτοιμη",
"This permanently deletes the encrypted credentials we sync (e.g., OPDS catalog passwords) on every device. Local copies are preserved. You will need to re-enter the sync passphrase or set a new one. Continue?": "Αυτό διαγράφει οριστικά τα κρυπτογραφημένα διαπιστευτήρια που συγχρονίζουμε (π.χ. κωδικούς καταλόγου OPDS) σε κάθε συσκευή. Τα τοπικά αντίγραφα διατηρούνται. Θα χρειαστεί να εισαγάγετε ξανά τη φράση πρόσβασης συγχρονισμού ή να ορίσετε μια νέα. Συνέχεια;",
"Sync passphrase forgotten — all encrypted fields cleared": "Η φράση πρόσβασης συγχρονισμού ξεχάστηκε — όλα τα κρυπτογραφημένα πεδία διαγράφηκαν",
"Sync passphrase": "Φράση πρόσβασης συγχρονισμού",
"Set passphrase": "Ορισμός φράσης πρόσβασης",
"Forgot passphrase": "Ξεχάσατε τη φράση πρόσβασης",
"Incorrect PIN": "Λανθασμένο PIN",
"App locked": "Η εφαρμογή είναι κλειδωμένη",
"Enter your PIN": "Εισαγάγετε το PIN σας",
"Readest is locked. Enter your 4-digit PIN to continue.": "Το Readest είναι κλειδωμένο. Εισαγάγετε το 4ψήφιο PIN σας για να συνεχίσετε.",
"PIN code": "Κωδικός PIN",
"Forgetting your PIN locks you out of this device. You'll need to clear the app's data to reset it.": "Αν ξεχάσετε το PIN σας, θα κλειδωθείτε από αυτή τη συσκευή. Θα χρειαστεί να διαγράψετε τα δεδομένα της εφαρμογής για να το επαναφέρετε.",
"Passphrase must be at least 8 characters": "Η φράση πρόσβασης πρέπει να έχει τουλάχιστον 8 χαρακτήρες",
"Passphrases do not match": "Οι φράσεις πρόσβασης δεν ταιριάζουν",
"Set sync passphrase": "Ορισμός φράσης πρόσβασης συγχρονισμού",
"Enter sync passphrase": "Εισαγωγή φράσης πρόσβασης συγχρονισμού",
"A sync passphrase encrypts your sensitive fields (like OPDS catalog credentials) before they sync. We never see this passphrase. Pick something memorable — there is no recovery without it.": "Μια φράση πρόσβασης συγχρονισμού κρυπτογραφεί τα ευαίσθητα πεδία σας (όπως διαπιστευτήρια καταλόγου OPDS) πριν συγχρονιστούν. Δεν βλέπουμε ποτέ αυτή τη φράση πρόσβασης. Επιλέξτε κάτι αξιομνημόνευτο — δεν υπάρχει ανάκτηση χωρίς αυτή.",
"Enter the sync passphrase you set on another device to decrypt your synced credentials.": "Εισαγάγετε τη φράση πρόσβασης συγχρονισμού που ορίσατε σε άλλη συσκευή για να αποκρυπτογραφήσετε τα συγχρονισμένα διαπιστευτήριά σας.",
"Confirm passphrase": "Επιβεβαίωση φράσης πρόσβασης",
"Unlock": "Ξεκλείδωμα",
"Set PIN": "Ορισμός PIN",
"Change PIN": "Αλλαγή PIN",
"Disable PIN": "Απενεργοποίηση PIN",
"Enter your current PIN, then choose a new 4-digit PIN.": "Εισαγάγετε το τρέχον PIN σας και, στη συνέχεια, επιλέξτε ένα νέο 4ψήφιο PIN.",
"Enter your current PIN to disable the app lock.": "Εισαγάγετε το τρέχον PIN σας για να απενεργοποιήσετε το κλείδωμα της εφαρμογής.",
"PIN must be {{length}} digits": "Το PIN πρέπει να έχει {{length}} ψηφία",
"PINs do not match": "Τα PIN δεν ταιριάζουν",
"Current PIN": "Τρέχον PIN",
"New PIN": "Νέο PIN",
"Confirm new PIN": "Επιβεβαίωση νέου PIN",
"Manage Sync": "Διαχείριση συγχρονισμού",
"Imported book files and library metadata": "Εισηγμένα αρχεία βιβλίων και μεταδεδομένα βιβλιοθήκης",
"Last-read position, bookmarks, and per-book preferences": "Τελευταία θέση ανάγνωσης, σελιδοδείκτες και προτιμήσεις ανά βιβλίο",
"Highlights and notes": "Επισημάνσεις και σημειώσεις",
"Fonts": "Γραμματοσειρές",
"Custom font files": "Προσαρμοσμένα αρχεία γραμματοσειρών",
"Backgrounds": "Φόντα",
"Custom background textures": "Προσαρμοσμένες υφές φόντου",
"OPDS catalogs": "Κατάλογοι OPDS",
"Saved catalog URLs and (encrypted) credentials": "Αποθηκευμένες διευθύνσεις καταλόγων και (κρυπτογραφημένα) διαπιστευτήρια",
"Wrong sync passphrase — synced credentials could not be decrypted": "Λανθασμένη φράση πρόσβασης συγχρονισμού — δεν ήταν δυνατή η αποκρυπτογράφηση των συγχρονισμένων διαπιστευτηρίων",
"Sync passphrase data on the server was reset. Re-encrypting your credentials under the new passphrase…": "Τα δεδομένα της φράσης πρόσβασης συγχρονισμού στον διακομιστή επαναφέρθηκαν. Επανακρυπτογράφηση των διαπιστευτηρίων σας με τη νέα φράση πρόσβασης…",
"Failed to decrypt synced credentials": "Αποτυχία αποκρυπτογράφησης των συγχρονισμένων διαπιστευτηρίων",
"Imported dictionary bundles and settings": "Εισηγμένα πακέτα λεξικών και ρυθμίσεις",
"Choose what syncs across your devices. Disabling a category stops this device from sending or receiving rows of that kind. Anything already on the server is left alone, re-enabling resumes from where you stopped.": "Επιλέξτε τι συγχρονίζεται μεταξύ των συσκευών σας. Η απενεργοποίηση μιας κατηγορίας σταματά αυτή τη συσκευή από το να στέλνει ή να λαμβάνει εγγραφές αυτού του τύπου. Ό,τι υπάρχει ήδη στον διακομιστή παραμένει ως έχει, η εκ νέου ενεργοποίηση συνεχίζει από εκεί που σταματήσατε.",
"Data Sync": "Συγχρονισμός δεδομένων",
"Manage Fonts": "Διαχείριση γραμματοσειρών",
"App settings": "Ρυθμίσεις εφαρμογής",
"Theme, highlight colours, integrations (KOSync, Readwise, Hardcover), and dictionary order": "Θέμα, χρώματα επισήμανσης, ενσωματώσεις (KOSync, Readwise, Hardcover) και σειρά λεξικών",
"Required while Dictionaries sync is enabled": "Απαιτείται όσο είναι ενεργός ο συγχρονισμός Λεξικών",
"Resets in {{hours}} hr {{minutes}} min": "Επαναφορά σε {{hours}} ώρ. {{minutes}} λεπ.",
"Pick a 4-digit PIN. You will need to enter it every time you open Readest. There is no way to recover a forgotten PIN. Clearing the app data is the only way to reset it.": "Επιλέξτε ένα 4ψήφιο PIN. Θα πρέπει να το εισάγετε κάθε φορά που ανοίγετε το Readest. Δεν υπάρχει τρόπος ανάκτησης ενός ξεχασμένου PIN. Η εκκαθάριση των δεδομένων της εφαρμογής είναι ο μοναδικός τρόπος επαναφοράς του.",
"Credentials": "Διαπιστευτήρια",
"Tokens, usernames, and passwords for OPDS, KOReader, Hardcover, and Readwise. When disabled, credentials remain on this device only and are never uploaded.": "Διακριτικά, ονόματα χρήστη και κωδικοί πρόσβασης για OPDS, KOReader, Hardcover και Readwise. Όταν είναι απενεργοποιημένο, τα διαπιστευτήρια παραμένουν μόνο σε αυτή τη συσκευή και δεν αποστέλλονται ποτέ.",
"Sensitive synced fields are encrypted before upload. Set a passphrase now or later when encryption is needed.": "Τα ευαίσθητα συγχρονισμένα πεδία κρυπτογραφούνται πριν τη μεταφόρτωση. Ορίστε μια φράση πρόσβασης τώρα ή αργότερα, όταν χρειαστεί η κρυπτογράφηση.",
"Saved to this account. You will be prompted for it when decrypting credentials.": "Αποθηκεύτηκε σε αυτόν τον λογαριασμό. Θα σας ζητηθεί όταν χρειαστεί να αποκρυπτογραφηθούν διαπιστευτήρια.",
"Reading progress on this device differs from \"{{deviceName}}\".": "Η πρόοδος ανάγνωσης σε αυτή τη συσκευή διαφέρει από το «{{deviceName}}».",
"This device": "Αυτή η συσκευή",
"Red": "Κόκκινο",
"Yellow": "Κίτρινο",
"Green": "Πράσινο",
"Blue": "Μπλε",
"Violet": "Βιολετί",
"Edit color": "Επεξεργασία χρώματος",
"Add custom color": "Προσθήκη προσαρμοσμένου χρώματος",
"Add label": "Προσθήκη ετικέτας",
"Choose color": "Επιλογή χρώματος",
"Connected to Hardcover. Last synced {{time}}.": "Συνδέθηκε στο Hardcover. Τελευταίος συγχρονισμός {{time}}.",
"Integrations": "Ενσωματώσεις",
"Hardcover": "Hardcover",
"Connected to Readwise. Last synced {{time}}.": "Συνδέθηκε στο Readwise. Τελευταίος συγχρονισμός {{time}}.",
"Readwise": "Readwise",
"Browse and download books from online catalogs.": "Περιηγηθείτε και κατεβάστε βιβλία από διαδικτυακούς καταλόγους.",
"Connected as {{user}}": "Συνδέθηκε ως {{user}}",
"Not connected": "Μη συνδεδεμένο",
"{{count}} catalog_one": "{{count}} κατάλογος",
"{{count}} catalog_other": "{{count}} κατάλογοι",
"No catalogs": "Δεν υπάρχουν κατάλογοι",
"Connect Readest to external services for sync, highlights, and catalogs.": "Συνδέστε το Readest με εξωτερικές υπηρεσίες για συγχρονισμό, επισημάνσεις και καταλόγους.",
"Reading Sync": "Συγχρονισμός ανάγνωσης",
"Content Sources": "Πηγές περιεχομένου",
"Scroll tabs": "Κύλιση καρτελών",
"Catalog actions": "Ενέργειες καταλόγου",
"File Content": "Περιεχόμενο αρχείου",
"Start your library": "Ξεκινήστε τη βιβλιοθήκη σας",
"Pick a book from your device to add it to your library.": "Επιλέξτε ένα βιβλίο από τη συσκευή σας για να το προσθέσετε στη βιβλιοθήκη σας.",
"Drop a book anywhere on this window, or pick one from your computer.": "Αφήστε ένα βιβλίο οπουδήποτε σε αυτό το παράθυρο ή επιλέξτε ένα από τον υπολογιστή σας.",
"Sign in to sync your library": "Συνδεθείτε για να συγχρονίσετε τη βιβλιοθήκη σας"
}
@@ -1,13 +1,57 @@
{
"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",
"Source Han Serif CN": "Source Han Serif",
"Huiwen-MinchoGBK": "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...",
"{{count}} pages left in chapter_one": "{{count}} page left in chapter",
"{{count}} pages left in chapter_other": "{{count}} pages left in chapter"
"{{count}} pages left in chapter_one": "<0>{{count}}</0><1> page left in chapter</1>",
"{{count}} pages left in chapter_other": "<0>{{count}}</0><1> pages left in chapter</1>",
"Deleted {{count}} file(s)_one": "Deleted {{count}} file",
"Deleted {{count}} file(s)_other": "Deleted {{count}} files",
"Failed to delete {{count}} file(s)_one": "Failed to delete {{count}} file",
"Failed to delete {{count}} file(s)_other": "Failed to delete {{count}} files",
"{{count}} selected_one": "{{count}} selected",
"{{count}} selected_other": "{{count}} selected",
"Are you sure to delete {{count}} selected file(s)?_one": "Are you sure to delete {{count}} selected file?",
"Are you sure to delete {{count}} selected file(s)?_other": "Are you sure to delete {{count}} selected files?",
"Successfully imported {{count}} book(s)_one": "Successfully imported {{count}} book",
"Successfully imported {{count}} book(s)_other": "Successfully imported {{count}} books",
"Set status for {{count}} book(s)_one": "Set status for {{count}} book",
"Set status for {{count}} book(s)_other": "Set status for {{count}} books",
"{{count}} book(s) synced_one": "{{count}} book synced",
"{{count}} book(s) synced_other": "{{count}} books synced",
"{{count}} active_one": "{{count}} active",
"{{count}} active_other": "{{count}} active",
"{{count}} downloads_one": "{{count}} download",
"{{count}} downloads_other": "{{count}} downloads",
"{{count}} days_one": "{{count}} day",
"{{count}} days_other": "{{count}} days",
"Expires in {{count}} days_one": "Expires in {{count}} day",
"Expires in {{count}} days_other": "Expires in {{count}} days",
"Expires in {{count}} hours_one": "Expires in {{count}} hour",
"Expires in {{count}} hours_other": "Expires in {{count}} hours",
"Attempts: {{count}}_one": "Attempts: {{count}}",
"Attempts: {{count}}_other": "Attempts: {{count}}",
"Failed to sync {{count}} OPDS catalog(s)_one": "Failed to sync {{count}} OPDS catalog",
"Failed to sync {{count}} OPDS catalog(s)_other": "Failed to sync {{count}} OPDS catalogs",
"Imported {{count}} dictionary_one": "Imported {{count}} dictionary",
"Imported {{count}} dictionary_other": "Imported {{count}} dictionaries",
"{{count}} catalog_one": "{{count}} catalog",
"{{count}} catalog_other": "{{count}} catalogs",
"Replaced {{count}} existing dictionary_one": "Replaced {{count}} existing dictionary",
"Replaced {{count}} existing dictionary_other": "Replaced {{count}} existing dictionaries",
"{{count}} books refreshed_one": "{{count}} book refreshed",
"{{count}} books refreshed_other": "{{count}} books refreshed",
"{{count}} failed_one": "{{count}} failed",
"{{count}} failed_other": "{{count}} failed",
"{{count}} items_one": "{{count}} item",
"{{count}} items_other": "{{count}} items",
"{{count}} new item(s) downloaded from OPDS_one": "{{count}} new item downloaded from OPDS",
"{{count}} new item(s) downloaded from OPDS_other": "{{count}} new items downloaded from OPDS",
"{{engine}}: {{count}} voices_one": "{{engine}}: {{count}} voice",
"{{engine}}: {{count}} voices_other": "{{engine}}: {{count}} voices"
}
@@ -69,7 +69,6 @@
"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",
@@ -83,14 +82,10 @@
"Send changes only": "Solo enviar cambios",
"Receive changes only": "Solo recibir cambios",
"Checksum Method": "Método de identificación",
"File Content (recommended)": "Contenido del archivo (recomendado)",
"File Name": "Nombre del archivo",
"Device Name": "Nombre del dispositivo",
"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",
@@ -129,11 +124,9 @@
"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.",
"Wikipedia": "Wikipedia",
"Writing Mode": "Modo de escritura",
"Your Library": "Tu biblioteca",
"TTS not supported for PDF": "TTS no es compatible con PDF",
"Override Book Font": "Sobrescribir fuente del libro",
"Apply to All Books": "Aplicar a todos los libros",
"Apply to This Book": "Aplicar a este libro",
@@ -200,9 +193,6 @@
"Token": "Código de verificación",
"Your OTP token": "Tu código OTP",
"Verify token": "Verificar código",
"Sign in with Google": "Iniciar sesión con Google",
"Sign in with Apple": "Iniciar sesión con Apple",
"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.",
"Community Support": "Soporte comunitario",
@@ -215,7 +205,6 @@
"RTL Direction": "Dirección RTL",
"Maximum Column Height": "Altura máxima de columna",
"Maximum Column Width": "Ancho máximo de columna",
"Continuous Scroll": "Desplazamiento continuo",
"Fullscreen": "Pantalla completa",
"No supported files found. Supported formats: {{formats}}": "No se encontraron archivos compatibles. Formatos compatibles: {{formats}}",
"Drop to Import Books": "Soltar para importar libros",
@@ -333,7 +322,6 @@
"No notes match your search": "No se encontraron notas",
"Search notes and excerpts...": "Buscar notas...",
"Sign in to Sync": "Inicia sesión para sincronizar",
"Synced at {{time}}": "Sincronizado a las {{time}}",
"Never synced": "Nunca sincronizado",
"Show Remaining Time": "Mostrar tiempo restante",
"{{time}} min left in chapter": "{{time}} min restantes en el capítulo",
@@ -368,12 +356,12 @@
"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",
"{{count}} pages left in chapter_one": "<0>{{count}}</0><1> página restante en el capítulo</1>",
"{{count}} pages left in chapter_many": "<0>{{count}}</0><1> páginas restantes en el capítulo</1>",
"{{count}} pages left in chapter_other": "<0>{{count}}</0><1> páginas restantes en el capítulo</1>",
"Show Remaining Pages": "Mostrar resto",
"Source Han Serif CN VF": "Source Han Serif",
"Huiwen-mincho": "Huiwen Mincho",
"Source Han Serif CN": "Source Han Serif",
"Huiwen-MinchoGBK": "Huiwen Mincho",
"KingHwa_OldSong": "KingHwa Song",
"Manage Subscription": "Gestionar suscripción",
"Coming Soon": "Próximamente",
@@ -606,11 +594,10 @@
"Size": "Tamaño",
"Cover": "Cubierta",
"Contain": "Contener",
"{{number}} pages left in chapter": "{{number}} páginas restantes en el capítulo",
"{{number}} pages left in chapter": "<0>{{number}}</0><1> páginas restantes en el capítulo</1>",
"Device": "Dispositivo",
"E-Ink Mode": "Modo E-Ink",
"Highlight Colors": "Colores de resaltado",
"Auto Screen Brightness": "Brillo automático de pantalla",
"Pagination": "Paginación",
"Disable Double Tap": "Deshabilitar Doble Toque",
"Tap to Paginate": "Tocar para Paginación",
@@ -645,7 +632,6 @@
"Strikethrough": "Tachado",
"Squiggly": "Ondulado",
"Outline": "Contorno",
"Save Current Color": "Guardar Color Actual",
"Quick Colors": "Colores Rápidos",
"Highlighter": "Resaltador",
"Save Book Cover": "Guardar Portada del Libro",
@@ -703,9 +689,7 @@
"Validating...": "Validando...",
"View All": "Ver todo",
"Forward": "Adelante",
"OPDS Catalog": "Catálogo OPDS",
"Home": "Inicio",
"Library": "Biblioteca",
"{{count}} items_one": "{{count}} elemento",
"{{count}} items_many": "{{count}} elementos",
"{{count}} items_other": "{{count}} elementos",
@@ -726,5 +710,732 @@
"Last": "Último",
"Cannot Load Page": "No se puede cargar la página",
"An error occurred": "Ocurrió un error",
"Online Library": "Biblioteca en línea"
"Online Library": "Biblioteca en línea",
"URL must start with http:// or https://": "URL debe comenzar con http:// o https://",
"Title, Author, Tag, etc...": "Titulo, Autor, Etiqueta, etc...",
"Query": "Consulta",
"Subject": "Asunto",
"Enter {{terms}}": "Ingrese {{terms}}",
"No search results found": "No se encontraron resultados de búsqueda",
"Failed to load OPDS feed: {{status}} {{statusText}}": "Error al cargar el feed OPDS: {{status}} {{statusText}}",
"Search in {{title}}": "Buscar en {{title}}",
"Manage Storage": "Administrar almacenamiento",
"Failed to load files": "Error al cargar los archivos",
"Deleted {{count}} file(s)_one": "Se eliminó {{count}} archivo",
"Deleted {{count}} file(s)_many": "Se eliminaron {{count}} archivos",
"Deleted {{count}} file(s)_other": "Se eliminaron {{count}} archivos",
"Failed to delete {{count}} file(s)_one": "Error al eliminar {{count}} archivo",
"Failed to delete {{count}} file(s)_many": "Error al eliminar {{count}} archivos",
"Failed to delete {{count}} file(s)_other": "Error al eliminar {{count}} archivos",
"Failed to delete files": "Error al eliminar los archivos",
"Total Files": "Total de archivos",
"Total Size": "Tamaño total",
"Quota": "Cuota",
"Used": "Usado",
"Files": "Archivos",
"Search files...": "Buscar archivos...",
"Newest First": "Más nuevos primero",
"Oldest First": "Más antiguos primero",
"Largest First": "Más grandes primero",
"Smallest First": "Más pequeños primero",
"Name A-Z": "Nombre AZ",
"Name Z-A": "Nombre ZA",
"{{count}} selected_one": "{{count}} seleccionado",
"{{count}} selected_many": "{{count}} seleccionados",
"{{count}} selected_other": "{{count}} seleccionados",
"Delete Selected": "Eliminar seleccionados",
"Created": "Creado",
"No files found": "No se encontraron archivos",
"No files uploaded yet": "Aún no se han subido archivos",
"files": "archivos",
"Page {{current}} of {{total}}": "Página {{current}} de {{total}}",
"Are you sure to delete {{count}} selected file(s)?_one": "¿Seguro que deseas eliminar {{count}} archivo seleccionado?",
"Are you sure to delete {{count}} selected file(s)?_many": "¿Seguro que deseas eliminar {{count}} archivos seleccionados?",
"Are you sure to delete {{count}} selected file(s)?_other": "¿Seguro que deseas eliminar {{count}} archivos seleccionados?",
"Cloud Storage Usage": "Uso de almacenamiento en la nube",
"Rename Group": "Renombrar grupo",
"From Directory": "Desde el directorio",
"Successfully imported {{count}} book(s)_one": "Se importó correctamente 1 libro",
"Successfully imported {{count}} book(s)_many": "Se importaron correctamente {{count}} libros",
"Successfully imported {{count}} book(s)_other": "Se importaron correctamente {{count}} libros",
"Count": "Cuenta",
"Start Page": "Página de inicio",
"Search in OPDS Catalog...": "Buscar en el catálogo OPDS...",
"Please log in to use advanced TTS features": "Por favor, inicie sesión para usar funciones avanzadas de TTS",
"Word limit of 30 words exceeded.": "Se superó el límite de 30 palabras.",
"Proofread": "Corrección",
"Current selection": "Selección actual",
"All occurrences in this book": "Todas las apariciones en este libro",
"All occurrences in your library": "Todas las apariciones en tu biblioteca",
"Selected text:": "Texto seleccionado:",
"Replace with:": "Reemplazar por:",
"Enter text...": "Introduce texto…",
"Case sensitive:": "Distinguir mayúsculas y minúsculas:",
"Scope:": "Ámbito:",
"Selection": "Selección",
"Library": "Biblioteca",
"Yes": "Sí",
"No": "No",
"Proofread Replacement Rules": "Reglas de reemplazo de corrección",
"Selected Text Rules": "Reglas de texto seleccionado",
"No selected text replacement rules": "No hay reglas de reemplazo para el texto seleccionado",
"Book Specific Rules": "Reglas específicas del libro",
"No book-level replacement rules": "No hay reglas de reemplazo a nivel de libro",
"Disable Quick Action": "Desactivar acción rápida",
"Enable Quick Action on Selection": "Activar acción rápida al seleccionar",
"None": "Ninguna",
"Annotation Tools": "Herramientas de anotación",
"Enable Quick Actions": "Activar acciones rápidas",
"Quick Action": "Acción rápida",
"Copy to Notebook": "Copiar al cuaderno",
"Copy text after selection": "Copiar texto después de la selección",
"Highlight text after selection": "Resaltar texto después de la selección",
"Annotate text after selection": "Anotar texto después de la selección",
"Search text after selection": "Buscar texto después de la selección",
"Look up text in dictionary after selection": "Buscar texto en el diccionario después de la selección",
"Translate text after selection": "Traducir texto después de la selección",
"Read text aloud after selection": "Leer texto en voz alta después de la selección",
"Proofread text after selection": "Corregir texto después de la selección",
"{{activeCount}} active, {{pendingCount}} pending": "{{activeCount}} activas, {{pendingCount}} pendientes",
"{{failedCount}} failed": "{{failedCount}} fallidas",
"Waiting...": "Esperando...",
"Failed": "Fallido",
"Completed": "Completado",
"Cancelled": "Cancelado",
"Retry": "Reintentar",
"Active": "Activas",
"Transfer Queue": "Cola de transferencias",
"Upload All": "Subir todos",
"Download All": "Descargar todos",
"Resume Transfers": "Reanudar transferencias",
"Pause Transfers": "Pausar transferencias",
"Pending": "Pendientes",
"No transfers": "Sin transferencias",
"Retry All": "Reintentar todos",
"Clear Completed": "Limpiar completados",
"Clear Failed": "Limpiar fallidos",
"Upload queued: {{title}}": "Subida en cola: {{title}}",
"Download queued: {{title}}": "Descarga en cola: {{title}}",
"Book not found in library": "Libro no encontrado en la biblioteca",
"Unknown error": "Error desconocido",
"Please log in to continue": "Inicia sesión para continuar",
"Cloud File Transfers": "Transferencias en la nube",
"Show Search Results": "Mostrar resultados",
"Search results for '{{term}}'": "Resultados para '{{term}}'",
"Close Search": "Cerrar búsqueda",
"Previous Result": "Resultado anterior",
"Next Result": "Resultado siguiente",
"Bookmarks": "Marcadores",
"Annotations": "Anotaciones",
"Show Results": "Mostrar resultados",
"Clear search": "Borrar búsqueda",
"Clear search history": "Borrar historial de búsqueda",
"Tap to Toggle Footer": "Toca para alternar pie de página",
"Show Current Time": "Mostrar hora actual",
"Use 24 Hour Clock": "Usar formato de 24 horas",
"Show Current Battery Status": "Mostrar el estado actual de la batería",
"Exported successfully": "Exportado con éxito",
"Book exported successfully.": "Libro exportado con éxito.",
"Failed to export the book.": "Error al exportar el libro.",
"Export Book": "Exportar libro",
"Whole word:": "Palabra completa:",
"Date Published": "Fecha de publicación",
"Only for TTS:": "Solo para TTS:",
"Uploaded": "Subido",
"Downloaded": "Descargado",
"Deleted": "Eliminado",
"Note:": "Nota:",
"Time:": "Hora:",
"Format Options": "Opciones de formato",
"Export Date": "Fecha de exportación",
"Chapter Titles": "Títulos de capítulos",
"Chapter Separator": "Separador de capítulos",
"Highlights": "Resaltados",
"Note Date": "Fecha de nota",
"Advanced": "Avanzado",
"Hide": "Ocultar",
"Show": "Mostrar",
"Use Custom Template": "Usar plantilla personalizada",
"Export Template": "Plantilla de exportación",
"Template Syntax:": "Sintaxis de plantilla:",
"Insert value": "Insertar valor",
"Format date (locale)": "Formatear fecha (regional)",
"Format date (custom)": "Formatear fecha (personalizado)",
"Conditional": "Condicional",
"Loop": "Bucle",
"Available Variables:": "Variables disponibles:",
"Book title": "Título del libro",
"Book author": "Autor del libro",
"Export date": "Fecha de exportación",
"Array of chapters": "Lista de capítulos",
"Chapter title": "Título del capítulo",
"Array of annotations": "Lista de anotaciones",
"Highlighted text": "Texto resaltado",
"Annotation note": "Nota de anotación",
"Date Format Tokens:": "Tokens de formato de fecha:",
"Year (4 digits)": "Año (4 dígitos)",
"Month (01-12)": "Mes (01-12)",
"Day (01-31)": "Día (01-31)",
"Hour (00-23)": "Hora (00-23)",
"Minute (00-59)": "Minuto (00-59)",
"Second (00-59)": "Segundo (00-59)",
"Show Source": "Mostrar fuente",
"No content to preview": "Sin contenido para previsualizar",
"Export": "Exportar",
"Set Timeout": "Establecer tiempo límite",
"Select Voice": "Seleccionar voz",
"Toggle Sticky Bottom TTS Bar": "Alternar barra TTS fija",
"Display what I'm reading on Discord": "Mostrar lo que estoy leyendo en Discord",
"Show on Discord": "Mostrar en Discord",
"Instant {{action}}": "{{action}} instantáneo",
"Instant {{action}} Disabled": "{{action}} instantáneo desactivado",
"Annotation": "Anotación",
"Reset Template": "Restablecer plantilla",
"Annotation style": "Estilo de anotación",
"Annotation color": "Color de anotación",
"Annotation time": "Hora de anotación",
"AI": "IA",
"Are you sure you want to re-index this book?": "¿Está seguro de que desea reindexar este libro?",
"Enable AI in Settings": "Habilitar IA en Configuración",
"Index This Book": "Indexar este libro",
"Enable AI search and chat for this book": "Habilitar búsqueda y chat con IA para este libro",
"Start Indexing": "Iniciar indexación",
"Indexing book...": "Indexando libro...",
"Preparing...": "Preparando...",
"Delete this conversation?": "¿Eliminar esta conversación?",
"No conversations yet": "Aún no hay conversaciones",
"Start a new chat to ask questions about this book": "Inicie un nuevo chat para hacer preguntas sobre este libro",
"Rename": "Renombrar",
"New Chat": "Nuevo chat",
"Chat": "Chat",
"Please enter a model ID": "Por favor, ingrese un ID de modelo",
"Model not available or invalid": "Modelo no disponible o inválido",
"Failed to validate model": "Error al validar el modelo",
"Couldn't connect to Ollama. Is it running?": "No se pudo conectar a Ollama. ¿Está ejecutándose?",
"Invalid API key or connection failed": "Clave API inválida o conexión fallida",
"Connection failed": "Conexión fallida",
"AI Assistant": "Asistente de IA",
"Enable AI Assistant": "Habilitar asistente de IA",
"Provider": "Proveedor",
"Ollama (Local)": "Ollama (Local)",
"AI Gateway (Cloud)": "Pasarela de IA (Nube)",
"Ollama Configuration": "Configuración de Ollama",
"Refresh Models": "Actualizar modelos",
"AI Model": "Modelo de IA",
"No models detected": "No se detectaron modelos",
"AI Gateway Configuration": "Configuración de pasarela de IA",
"Choose from a selection of high-quality, economical AI models. You can also bring your own model by selecting \"Custom Model\" below.": "Elija entre una selección de modelos de IA de alta calidad y económicos. También puede usar su propio modelo seleccionando \"Modelo personalizado\" a continuación.",
"API Key": "Clave API",
"Get Key": "Obtener clave",
"Model": "Modelo",
"Custom Model...": "Modelo personalizado...",
"Custom Model ID": "ID de modelo personalizado",
"Validate": "Validar",
"Model available": "Modelo disponible",
"Connection": "Conexión",
"Test Connection": "Probar conexión",
"Connected": "Conectado",
"Custom Colors": "Colores personalizados",
"Color E-Ink Mode": "Modo E-Ink a color",
"Reading Ruler": "Regla de lectura",
"Enable Reading Ruler": "Habilitar regla de lectura",
"Lines to Highlight": "Líneas a resaltar",
"Ruler Color": "Color de la regla",
"Command Palette": "Paleta de comandos",
"Search settings and actions...": "Buscar ajustes y acciones...",
"No results found for": "No se encontraron resultados para",
"Type to search settings and actions": "Escribe para buscar ajustes y acciones",
"Recent": "Reciente",
"navigate": "navegar",
"select": "seleccionar",
"close": "cerrar",
"Search Settings": "Buscar ajustes",
"Page Margins": "Márgenes de página",
"AI Provider": "Proveedor de IA",
"Ollama URL": "URL de Ollama",
"Ollama Model": "Modelo de Ollama",
"AI Gateway Model": "Modelo de AI Gateway",
"Actions": "Acciones",
"Navigation": "Navegación",
"Set status for {{count}} book(s)_one": "Establecer estado para {{count}} libro",
"Set status for {{count}} book(s)_other": "Establecer estado para {{count}} libros",
"Mark as Unread": "Marcar como no leído",
"Mark as Finished": "Marcar como terminado",
"Finished": "Terminado",
"Unread": "Sin leer",
"Clear Status": "Borrar estado",
"Status": "Estado",
"Set status for {{count}} book(s)_many": "Establecer estado para {{count}} libros",
"Loading": "Cargando...",
"Exit Paragraph Mode": "Salir del modo de párrafo",
"Paragraph Mode": "Modo de párrafo",
"Embedding Model": "Modelo de incrustación",
"{{count}} book(s) synced_one": "{{count}} libro sincronizado",
"{{count}} book(s) synced_many": "{{count}} libros sincronizados",
"{{count}} book(s) synced_other": "{{count}} libros sincronizados",
"Unable to start RSVP": "No se puede iniciar RSVP",
"RSVP not supported for PDF": "RSVP no es compatible con PDF",
"Select Chapter": "Seleccionar capítulo",
"Context": "Contexto",
"Ready": "Listo",
"Chapter Progress": "Progreso del capítulo",
"words": "palabras",
"{{time}} left": "faltan {{time}}",
"Reading progress": "Progreso de lectura",
"Skip back 15 words": "Retroceder 15 palabras",
"Back 15 words (Shift+Left)": "Retroceder 15 palabras (Shift+Izquierda)",
"Pause (Space)": "Pausa (Espacio)",
"Play (Space)": "Reproducir (Espacio)",
"Skip forward 15 words": "Adelantar 15 palabras",
"Forward 15 words (Shift+Right)": "Adelantar 15 palabras (Shift+Derecha)",
"Decrease speed": "Disminuir velocidad",
"Slower (Left/Down)": "Más lento (Izquierda/Abajo)",
"Increase speed": "Aumentar velocidad",
"Faster (Right/Up)": "Más rápido (Derecha/Arriba)",
"Start RSVP Reading": "Iniciar lectura RSVP",
"Choose where to start reading": "Elegir dónde empezar a leer",
"From Chapter Start": "Desde el inicio del capítulo",
"Start reading from the beginning of the chapter": "Empezar a leer desde el principio del capítulo",
"Resume": "Reanudar",
"Continue from where you left off": "Continuar desde donde lo dejaste",
"From Current Page": "Desde la página actual",
"Start from where you are currently reading": "Empezar desde donde estás leyendo actualmente",
"From Selection": "Desde la selección",
"Speed Reading Mode": "Modo de lectura rápida",
"Scroll left": "Desplazar a la izquierda",
"Scroll right": "Desplazar a la derecha",
"Library Sync Progress": "Progreso de sincronización de la biblioteca",
"Back to library": "Volver a la biblioteca",
"Group by...": "Agrupar por...",
"Export as Plain Text": "Exportar como texto sin formato",
"Export as Markdown": "Exportar como Markdown",
"Show Page Navigation Buttons": "Botones de navegación",
"Page {{number}}": "Página {{number}}",
"highlight": "resaltado",
"underline": "subrayado",
"squiggly": "ondulado",
"red": "rojo",
"violet": "violeta",
"blue": "azul",
"green": "verde",
"yellow": "amarillo",
"Select {{style}} style": "Seleccionar estilo {{style}}",
"Select {{color}} color": "Seleccionar color {{color}}",
"Close Book": "Cerrar libro",
"Speed Reading": "Lectura rápida",
"Close Speed Reading": "Cerrar lectura rápida",
"Authors": "Autores",
"Books": "Libros",
"Groups": "Grupos",
"Back to TTS Location": "Volver a la ubicación de TTS",
"Metadata": "Metadatos",
"Image viewer": "Visor de imágenes",
"Previous Image": "Imagen anterior",
"Next Image": "Imagen siguiente",
"Zoomed": "Con zoom",
"Zoom level": "Nivel de zoom",
"Table viewer": "Visor de tablas",
"Unable to connect to Readwise. Please check your network connection.": "No se pudo conectar a Readwise. Por favor, comprueba tu conexión a la red.",
"Invalid Readwise access token": "Token de acceso a Readwise no válido",
"Disconnected from Readwise": "Desconectado de Readwise",
"Never": "Nunca",
"Sync Enabled": "Sincronización habilitada",
"Disconnect": "Desconectar",
"Connect your Readwise account to sync highlights.": "Conecta tu cuenta de Readwise para sincronizar los resaltados.",
"Get your access token at": "Obtén tu token de acceso en",
"Access Token": "Token de acceso",
"Paste your Readwise access token": "Pega tu token de acceso de Readwise",
"Readwise Sync": "Sincronización con Readwise",
"Push Highlights": "Enviar resaltados",
"Highlights synced to Readwise": "Resaltados sincronizados con Readwise",
"Readwise sync failed: no internet connection": "Error en la sincronización con Readwise: sin conexión a internet",
"Readwise sync failed: {{error}}": "Error en la sincronización con Readwise: {{error}}",
"System Screen Brightness": "Brillo de pantalla del sistema",
"Page:": "Página:",
"Page: {{number}}": "Página: {{number}}",
"Annotation page number": "Número de página de la anotación",
"Translating...": "Traduciendo...",
"Show Battery Percentage": "Mostrar porcentaje de batería",
"Hide Scrollbar": "Ocultar barra de desplazamiento",
"Skip to last reading position": "Ir a la última posición de lectura",
"Show context": "Mostrar contexto",
"Hide context": "Ocultar contexto",
"Decrease font size": "Reducir tamaño de fuente",
"Increase font size": "Aumentar tamaño de fuente",
"Focus": "Enfoque",
"Theme color": "Color del tema",
"Import failed": "Error de importación",
"Available Formatters:": "Formateadores disponibles:",
"Format date": "Formatear fecha",
"Markdown block quote (> per line)": "Cita en bloque Markdown (> por línea)",
"Newlines to <br>": "Saltos de línea a <br>",
"Change case": "Cambiar mayúsculas/minúsculas",
"Trim whitespace": "Recortar espacios",
"Truncate to n characters": "Truncar a n caracteres",
"Replace text": "Reemplazar texto",
"Fallback value": "Valor alternativo",
"Get length": "Obtener longitud",
"First/last element": "Primer/último elemento",
"Join array": "Unir array",
"Backup failed: {{error}}": "Error en la copia de seguridad: {{error}}",
"Select Backup": "Seleccionar copia de seguridad",
"Restore failed: {{error}}": "Error en la restauración: {{error}}",
"Backup & Restore": "Copia de seguridad y restauración",
"Create a backup of your library or restore from a previous backup. Restoring will merge with your current library.": "Crea una copia de seguridad de tu biblioteca o restaura desde una copia anterior. La restauración se fusionará con tu biblioteca actual.",
"Backup Library": "Hacer copia de seguridad",
"Restore Library": "Restaurar biblioteca",
"Creating backup...": "Creando copia de seguridad...",
"Restoring library...": "Restaurando biblioteca...",
"{{current}} of {{total}} items": "{{current}} de {{total}} elementos",
"Backup completed successfully!": "¡Copia de seguridad completada!",
"Restore completed successfully!": "¡Restauración completada!",
"Your library has been saved to the selected location.": "Tu biblioteca se ha guardado en la ubicación seleccionada.",
"{{added}} books added, {{updated}} books updated.": "{{added}} libros añadidos, {{updated}} libros actualizados.",
"Operation failed": "La operación falló",
"Loading library...": "Cargando biblioteca...",
"{{count}} books refreshed_one": "{{count}} libro actualizado",
"{{count}} books refreshed_many": "{{count}} libros actualizados",
"{{count}} books refreshed_other": "{{count}} libros actualizados",
"Failed to refresh metadata": "Error al actualizar metadatos",
"Refresh Metadata": "Actualizar metadatos",
"Keyboard Shortcuts": "Atajos de teclado",
"View all keyboard shortcuts": "Ver todos los atajos de teclado",
"Switch Sidebar Tab": "Cambiar pestaña de la barra lateral",
"Toggle Notebook": "Mostrar/ocultar cuaderno",
"Search in Book": "Buscar en el libro",
"Toggle Scroll Mode": "Alternar modo desplazamiento",
"Toggle Select Mode": "Alternar modo selección",
"Toggle Bookmark": "Alternar marcador",
"Toggle Text to Speech": "Alternar texto a voz",
"Play / Pause TTS": "Reproducir / Pausar TTS",
"Toggle Paragraph Mode": "Alternar modo párrafo",
"Highlight Selection": "Resaltar selección",
"Underline Selection": "Subrayar selección",
"Annotate Selection": "Anotar selección",
"Search Selection": "Buscar selección",
"Copy Selection": "Copiar selección",
"Translate Selection": "Traducir selección",
"Dictionary Lookup": "Buscar en diccionario",
"Read Aloud Selection": "Leer selección en voz alta",
"Proofread Selection": "Corregir selección",
"Open Settings": "Abrir ajustes",
"Open Command Palette": "Abrir paleta de comandos",
"Show Keyboard Shortcuts": "Mostrar atajos de teclado",
"Open Books": "Abrir libros",
"Toggle Fullscreen": "Alternar pantalla completa",
"Close Window": "Cerrar ventana",
"Quit App": "Salir de la aplicación",
"Go Left / Previous Page": "Ir a la izquierda / Página anterior",
"Go Right / Next Page": "Ir a la derecha / Página siguiente",
"Go Up": "Ir arriba",
"Go Down": "Ir abajo",
"Previous Chapter": "Capítulo anterior",
"Next Chapter": "Capítulo siguiente",
"Scroll Half Page Down": "Desplazar media página abajo",
"Scroll Half Page Up": "Desplazar media página arriba",
"Save Note": "Guardar nota",
"Single Section Scroll": "Desplazamiento por sección",
"General": "General",
"Text to Speech": "Texto a voz",
"Zoom": "Zoom",
"Window": "Ventana",
"Your Bookshelf": "Tu estantería",
"TTS": "Lectura en voz alta",
"Media Info": "Info multimedia",
"Update Frequency": "Frecuencia de actualización",
"Every Sentence": "Cada oración",
"Every Paragraph": "Cada párrafo",
"Every Chapter": "Cada capítulo",
"TTS Media Info Update Frequency": "Frecuencia de actualización de info multimedia TTS",
"Select reading speed": "Seleccionar velocidad de lectura",
"Drag to seek": "Arrastra para buscar",
"Punctuation Delay": "Retraso de puntuación",
"Please confirm that this OPDS connection will be proxied through Readest servers on the web app before continuing.": "Confirme que esta conexión OPDS se enrutará a través de los servidores de Readest en la aplicación web antes de continuar.",
"Custom Headers (optional)": "Encabezados personalizados (opcional)",
"Add one header per line using \"Header-Name: value\".": "Añada un encabezado por línea usando \"Header-Name: value\".",
"I understand this OPDS connection will be proxied through Readest servers on the web app. If I do not trust Readest with these credentials or headers, I should use the native app instead.": "Entiendo que esta conexión OPDS se enrutará a través de los servidores de Readest en la aplicación web. Si no confío en Readest con estas credenciales o encabezados, debería usar la aplicación nativa.",
"Unable to connect to Hardcover. Please check your network connection.": "No se puede conectar a Hardcover. Compruebe su conexión de red.",
"Invalid Hardcover API token": "Token de API de Hardcover no válido",
"Disconnected from Hardcover": "Desconectado de Hardcover",
"Connect your Hardcover account to sync reading progress and notes.": "Conecte su cuenta de Hardcover para sincronizar el progreso de lectura y las notas.",
"Get your API token from hardcover.app → Settings → API.": "Obtenga su token de API en hardcover.app → Ajustes → API.",
"API Token": "Token de API",
"Paste your Hardcover API token": "Pegue su token de API de Hardcover",
"Hardcover Sync": "Sincronización de Hardcover",
"Push Notes": "Enviar notas",
"No annotations or excerpts to sync for this book.": "No hay anotaciones ni extractos para sincronizar en este libro.",
"Configure Hardcover in Settings first.": "Configure Hardcover en los ajustes primero.",
"No new Hardcover note changes to sync.": "No hay nuevos cambios en notas de Hardcover para sincronizar.",
"Hardcover synced: {{inserted}} new, {{updated}} updated, {{skipped}} unchanged": "Hardcover sincronizado: {{inserted}} nuevos, {{updated}} actualizados, {{skipped}} sin cambios",
"Hardcover notes sync failed: {{error}}": "Error al sincronizar notas de Hardcover: {{error}}",
"Reading progress synced to Hardcover": "Progreso de lectura sincronizado con Hardcover",
"Hardcover progress sync failed: {{error}}": "Error al sincronizar progreso de Hardcover: {{error}}",
"ISBN": "ISBN",
"Keep existing source identifier": "Mantener el identificador de origen existente",
"Toggle Toolbar": "Alternar barra de herramientas",
"Split Hyphens": "Dividir guiones",
"Apply Theme Colors to PDF": "Aplicar colores del tema al PDF",
"Name": "Nombre",
"Remove": "Eliminar",
"Edit OPDS Catalog": "Editar catálogo OPDS",
"Save Changes": "Guardar cambios",
"Use Book Layout": "Usar el diseño del libro",
"End of this section. Continue to the next.": "Fin de esta sección. Continuar a la siguiente.",
"Auto-download": "Descarga automática",
"Auto-download new items": "Descargar nuevos elementos automáticamente",
"Automatically download new publications when the app syncs": "Descargar nuevas publicaciones automáticamente cuando la aplicación se sincroniza",
"{{count}} new item(s) downloaded from OPDS_one": "Se descargó {{count}} nuevo elemento desde OPDS",
"{{count}} new item(s) downloaded from OPDS_many": "Se descargaron {{count}} nuevos elementos desde OPDS",
"{{count}} new item(s) downloaded from OPDS_other": "Se descargaron {{count}} nuevos elementos desde OPDS",
"Failed to sync {{count}} OPDS catalog(s)_one": "No se pudo sincronizar {{count}} catálogo OPDS",
"Failed to sync {{count}} OPDS catalog(s)_many": "No se pudieron sincronizar {{count}} catálogos OPDS",
"Failed to sync {{count}} OPDS catalog(s)_other": "No se pudieron sincronizar {{count}} catálogos OPDS",
"Last synced {{when}}": "Última sincronización {{when}}",
"Failed downloads": "Descargas fallidas",
"No failed downloads": "No hay descargas fallidas",
"Attempts: {{count}}_one": "Intentos: {{count}}",
"Attempts: {{count}}_many": "Intentos: {{count}}",
"Attempts: {{count}}_other": "Intentos: {{count}}",
"Skip": "Omitir",
"Skip all": "Omitir todos",
"Retry all": "Reintentar todos",
"{{count}} failed_one": "{{count}} fallida",
"{{count}} failed_many": "{{count}} fallidas",
"{{count}} failed_other": "{{count}} fallidas",
"(none)": "(ninguno)",
"Identifiers": "Identificadores",
"Read (Stream)": "Leer (Streaming)",
"Failed to start stream": "No se pudo iniciar el streaming",
"No dictionaries enabled": "No hay diccionarios habilitados",
"Enable a dictionary in Settings → Language → Dictionaries.": "Habilita un diccionario en Ajustes → Idioma → Diccionarios.",
"Wiktionary": "Wiktionary",
"Drag to reorder": "Arrastrar para reordenar",
"Built-in": "Integrado",
"Bundle is missing on this device. Re-import to use it.": "El paquete falta en este dispositivo. Vuelve a importarlo para usarlo.",
"This dictionary format is not supported.": "Este formato de diccionario no es compatible.",
"MDict": "MDict",
"StarDict": "StarDict",
"Imported {{count}} dictionary_one": "Se importó {{count}} diccionario",
"Imported {{count}} dictionary_many": "Se importaron {{count}} diccionarios",
"Imported {{count}} dictionary_other": "Se importaron {{count}} diccionarios",
"Skipped incomplete bundles: {{names}}": "Se omitieron paquetes incompletos: {{names}}",
"Failed to import dictionary: {{message}}": "Error al importar el diccionario: {{message}}",
"Dictionaries": "Diccionarios",
"Delete Dictionary": "Eliminar diccionario",
"Importing…": "Importando…",
"Import Dictionary": "Importar diccionario",
"No dictionaries available.": "No hay diccionarios disponibles.",
"StarDict bundles need .ifo, .idx, and .dict.dz files (.syn optional).": "Los paquetes StarDict requieren archivos .ifo, .idx y .dict.dz (.syn opcional).",
"Select all the bundle files together when importing.": "Selecciona todos los archivos del paquete juntos al importar.",
"Manage Dictionaries": "Administrar diccionarios",
"Select Dictionary Files": "Seleccionar archivos de diccionario",
"Read on Wikipedia →": "Leer en Wikipedia →",
"This link can't be opened": "No se puede abrir este enlace",
"The annotation link is missing required information. The original link may have been truncated.": "Al enlace de anotación le faltan datos necesarios. Es posible que el enlace original se haya truncado.",
"Go to Readest": "Ir a Readest",
"Open-source ebook reader for everyone, on every device.": "Lector de libros electrónicos de código abierto para todos, en cualquier dispositivo.",
"Open in Readest": "Abrir en Readest",
"If Readest didn't open automatically, choose an option below:": "Si Readest no se abrió automáticamente, elige una opción:",
"Continue reading where you left off.": "Continúa leyendo donde lo dejaste.",
"Readest logo": "Logotipo de Readest",
"Opening Readest...": "Abriendo Readest…",
"Open in Readest app": "Abrir en la app de Readest",
"Continue in browser": "Continuar en el navegador",
"Don't have Readest?": "¿No tienes Readest?",
"Book not in your library": "El libro no está en tu biblioteca",
"Share Book": "Compartir libro",
"Could not create share link": "No se pudo crear el enlace de compartir",
"Link copied": "Enlace copiado",
"Could not copy link": "No se pudo copiar el enlace",
"Share revoked": "Compartir revocado",
"Could not revoke share": "No se pudo revocar el compartir",
"Uploading book…": "Subiendo libro…",
"Generating…": "Generando…",
"Generate share link": "Generar enlace de compartir",
"Share URL": "URL de compartir",
"Copy link": "Copiar enlace",
"Copied": "Copiado",
"Share via…": "Compartir vía…",
"Expires {{date}}": "Caduca el {{date}}",
"Revoking…": "Revocando…",
"Revoke share": "Revocar compartir",
"Sign in to share books": "Inicia sesión para compartir libros",
"Expiring soon": "Caduca pronto",
"Missing share token": "Falta el token de compartir",
"Could not add to your library": "No se pudo añadir a tu biblioteca",
"This share link is no longer available": "Este enlace ya no está disponible",
"The original link may have expired or been revoked.": "Es posible que el enlace original haya caducado o se haya revocado.",
"Get Readest": "Obtener Readest",
"Loading shared book…": "Cargando libro compartido…",
"Adding…": "Añadiendo…",
"Add to my library": "Añadir a mi biblioteca",
"Could not load your shares": "No se pudieron cargar tus compartidos",
"Revoked": "Revocado",
"Expired": "Caducado",
"Shared books": "Libros compartidos",
"You haven't shared any books yet": "Aún no has compartido ningún libro",
"Open a book and tap Share to send it to a friend.": "Abre un libro y pulsa Compartir para enviárselo a un amigo.",
"{{count}} active_one": "{{count}} activo",
"{{count}} active_many": "{{count}} activos",
"{{count}} active_other": "{{count}} activos",
"{{count}} downloads_one": "{{count}} descarga",
"{{count}} downloads_many": "{{count}} descargas",
"{{count}} downloads_other": "{{count}} descargas",
"starts at saved page": "comienza en la página guardada",
"More actions": "Más acciones",
"Loading…": "Cargando…",
"Load more": "Cargar más",
"Could not load shared book": "No se pudo cargar el libro compartido",
"The share link is missing required information.": "Al enlace de compartir le faltan datos necesarios.",
"Please check your connection and try again.": "Comprueba tu conexión y vuelve a intentarlo.",
"Sign in to import shared books": "Inicia sesión para importar libros compartidos",
"Already in your library": "Ya está en tu biblioteca",
"Added to your library": "Añadido a tu biblioteca",
"Could not import shared book": "No se pudo importar el libro compartido",
"Manage Shared Links": "Gestionar enlaces compartidos",
"Expires in": "Caduca en",
"{{count}} days_one": "1 día",
"{{count}} days_many": "{{count}} días",
"{{count}} days_other": "{{count}} días",
"Expires in {{count}} days_one": "Caduca en 1 día",
"Expires in {{count}} days_many": "Caduca en {{count}} días",
"Expires in {{count}} days_other": "Caduca en {{count}} días",
"Expires in {{count}} hours_one": "Caduca en 1 hora",
"Expires in {{count}} hours_many": "Caduca en {{count}} horas",
"Expires in {{count}} hours_other": "Caduca en {{count}} horas",
"Open in app": "Abrir en la app",
"Shared with you": "Compartido contigo",
"Downloading… {{percent}}%": "Descargando… {{percent}}%",
"Import progress": "Progreso de la importación",
"Share reading progress": "Compartir el progreso de lectura",
"Includes your reading progress": "Incluye tu progreso de lectura",
"Synced {{time}}": "Sincronizado {{time}}",
"Sync Info": "Información de sincronización",
"Last Synced": "Última sincronización",
"DICT": "DICT",
"Slob": "Slob",
"DICT bundles need a .index file and a .dict.dz file.": "Los paquetes DICT necesitan un archivo .index y un archivo .dict.dz.",
"Slob bundles need a .slob file.": "Los paquetes Slob necesitan un archivo .slob.",
"URL template must start with http(s):// and contain %WORD%.": "La plantilla de URL debe empezar con http(s):// y contener %WORD%.",
"Web": "Web",
"Add Web Search": "Añadir búsqueda web",
"Edit Web Search": "Editar búsqueda web",
"e.g. Google": "p. ej. Google",
"URL Template": "Plantilla de URL",
"Use %WORD% where the looked-up word should appear.": "Use %WORD% donde debe aparecer la palabra buscada.",
"Open the search result in your browser:": "Abre el resultado de búsqueda en tu navegador:",
"Open in {{name}}": "Abrir en {{name}}",
"Book Fingerprint": "Huella del libro",
"Search the web": "Buscar en la web",
"App deeplink (readest://)": "Enlace profundo de la app (readest://)",
"Universal web link (https://)": "Enlace web universal (https://)",
"Name cannot be empty.": "El nombre no puede estar vacío.",
"Replaced {{count}} existing dictionary_one": "Se reemplazó {{count}} diccionario existente",
"Replaced {{count}} existing dictionary_many": "Se reemplazaron {{count}} diccionarios existentes",
"Replaced {{count}} existing dictionary_other": "Se reemplazaron {{count}} diccionarios existentes",
"Cancel Edit": "Cancelar edición",
"Edit Dictionary": "Editar diccionario",
"MDict bundles use .mdx files; companion .mdd and .css files are optional.": "Los paquetes MDict usan archivos .mdx; los archivos .mdd y .css complementarios son opcionales.",
"Dictionary name": "Nombre del diccionario",
"This audio can't play here — the dictionary uses an outdated format. Try one with Opus, MP3, or WAV audio.": "Este audio no se puede reproducir aquí — el diccionario usa un formato obsoleto. Prueba uno con audio Opus, MP3 o WAV.",
"File uploaded: {{title}}": "Archivo subido: {{title}}",
"File downloaded: {{title}}": "Archivo descargado: {{title}}",
"Deleted cloud copy of the file: {{title}}": "Copia en la nube del archivo eliminada: {{title}}",
"Failed to upload file: {{title}}": "Error al subir archivo: {{title}}",
"Failed to download file: {{title}}": "Error al descargar archivo: {{title}}",
"Failed to delete cloud copy of the file: {{title}}": "Error al eliminar la copia en la nube del archivo: {{title}}",
"{{percentage}}% used": "{{percentage}}% usado",
"Set PIN…": "Establecer PIN…",
"Require a 4-digit PIN to open Readest": "Requerir un PIN de 4 dígitos para abrir Readest",
"Change PIN…": "Cambiar PIN…",
"Disable PIN…": "Desactivar PIN…",
"Sync passphrase ready": "Frase de contraseña de sincronización lista",
"This permanently deletes the encrypted credentials we sync (e.g., OPDS catalog passwords) on every device. Local copies are preserved. You will need to re-enter the sync passphrase or set a new one. Continue?": "Esto elimina permanentemente las credenciales cifradas que sincronizamos (p. ej., contraseñas del catálogo OPDS) en cada dispositivo. Se conservan las copias locales. Tendrás que volver a introducir la frase de contraseña de sincronización o establecer una nueva. ¿Continuar?",
"Sync passphrase forgotten — all encrypted fields cleared": "Frase de contraseña de sincronización olvidada — todos los campos cifrados se han borrado",
"Sync passphrase": "Frase de contraseña de sincronización",
"Set passphrase": "Establecer frase de contraseña",
"Forgot passphrase": "Olvidé la frase de contraseña",
"Incorrect PIN": "PIN incorrecto",
"App locked": "Aplicación bloqueada",
"Enter your PIN": "Ingresa tu PIN",
"Readest is locked. Enter your 4-digit PIN to continue.": "Readest está bloqueado. Ingresa tu PIN de 4 dígitos para continuar.",
"PIN code": "Código PIN",
"Forgetting your PIN locks you out of this device. You'll need to clear the app's data to reset it.": "Si olvidas tu PIN, te bloquearás en este dispositivo. Tendrás que borrar los datos de la aplicación para restablecerlo.",
"Passphrase must be at least 8 characters": "La frase de contraseña debe tener al menos 8 caracteres",
"Passphrases do not match": "Las frases de contraseña no coinciden",
"Set sync passphrase": "Establecer frase de contraseña de sincronización",
"Enter sync passphrase": "Introduce la frase de contraseña de sincronización",
"A sync passphrase encrypts your sensitive fields (like OPDS catalog credentials) before they sync. We never see this passphrase. Pick something memorable — there is no recovery without it.": "Una frase de contraseña de sincronización cifra tus campos sensibles (como las credenciales del catálogo OPDS) antes de sincronizarlos. Nunca vemos esta frase de contraseña. Elige algo memorable — no hay recuperación sin ella.",
"Enter the sync passphrase you set on another device to decrypt your synced credentials.": "Introduce la frase de contraseña de sincronización que estableciste en otro dispositivo para descifrar tus credenciales sincronizadas.",
"Confirm passphrase": "Confirmar frase de contraseña",
"Unlock": "Desbloquear",
"Set PIN": "Establecer PIN",
"Change PIN": "Cambiar PIN",
"Disable PIN": "Desactivar PIN",
"Enter your current PIN, then choose a new 4-digit PIN.": "Ingresa tu PIN actual y, a continuación, elige un nuevo PIN de 4 dígitos.",
"Enter your current PIN to disable the app lock.": "Ingresa tu PIN actual para desactivar el bloqueo de la aplicación.",
"PIN must be {{length}} digits": "El PIN debe tener {{length}} dígitos",
"PINs do not match": "Los PIN no coinciden",
"Current PIN": "PIN actual",
"New PIN": "Nuevo PIN",
"Confirm new PIN": "Confirmar nuevo PIN",
"Manage Sync": "Gestionar sincronización",
"Imported book files and library metadata": "Archivos de libros importados y metadatos de la biblioteca",
"Last-read position, bookmarks, and per-book preferences": "Última posición de lectura, marcadores y preferencias por libro",
"Highlights and notes": "Resaltados y notas",
"Fonts": "Fuentes",
"Custom font files": "Archivos de fuentes personalizadas",
"Backgrounds": "Fondos",
"Custom background textures": "Texturas de fondo personalizadas",
"OPDS catalogs": "Catálogos OPDS",
"Saved catalog URLs and (encrypted) credentials": "URLs de catálogos guardadas y credenciales (cifradas)",
"Wrong sync passphrase — synced credentials could not be decrypted": "Frase de contraseña de sincronización incorrecta — no se pudieron descifrar las credenciales sincronizadas",
"Sync passphrase data on the server was reset. Re-encrypting your credentials under the new passphrase…": "Se restablecieron los datos de la frase de contraseña de sincronización en el servidor. Volviendo a cifrar tus credenciales con la nueva frase…",
"Failed to decrypt synced credentials": "No se pudieron descifrar las credenciales sincronizadas",
"Imported dictionary bundles and settings": "Paquetes de diccionarios importados y ajustes",
"Choose what syncs across your devices. Disabling a category stops this device from sending or receiving rows of that kind. Anything already on the server is left alone, re-enabling resumes from where you stopped.": "Elige qué se sincroniza entre tus dispositivos. Al desactivar una categoría, este dispositivo deja de enviar o recibir entradas de ese tipo. Lo que ya está en el servidor se mantiene intacto, y al reactivarla se reanuda desde donde lo dejaste.",
"Data Sync": "Sincronización de datos",
"Manage Fonts": "Administrar fuentes",
"App settings": "Ajustes de la aplicación",
"Theme, highlight colours, integrations (KOSync, Readwise, Hardcover), and dictionary order": "Tema, colores de resaltado, integraciones (KOSync, Readwise, Hardcover) y orden de los diccionarios",
"Required while Dictionaries sync is enabled": "Necesario mientras la sincronización de Diccionarios esté activada",
"Resets in {{hours}} hr {{minutes}} min": "Se restablece en {{hours}} h {{minutes}} min",
"Pick a 4-digit PIN. You will need to enter it every time you open Readest. There is no way to recover a forgotten PIN. Clearing the app data is the only way to reset it.": "Elige un PIN de 4 dígitos. Tendrás que introducirlo cada vez que abras Readest. No hay forma de recuperar un PIN olvidado. Borrar los datos de la aplicación es la única forma de restablecerlo.",
"Credentials": "Credenciales",
"Tokens, usernames, and passwords for OPDS, KOReader, Hardcover, and Readwise. When disabled, credentials remain on this device only and are never uploaded.": "Tokens, nombres de usuario y contraseñas de OPDS, KOReader, Hardcover y Readwise. Si está desactivado, las credenciales permanecen solo en este dispositivo y nunca se cargan.",
"Sensitive synced fields are encrypted before upload. Set a passphrase now or later when encryption is needed.": "Los campos sincronizados sensibles se cifran antes de subirlos. Establece una frase de contraseña ahora o más tarde, cuando se necesite el cifrado.",
"Saved to this account. You will be prompted for it when decrypting credentials.": "Guardada en esta cuenta. Se te pedirá cuando se necesite descifrar credenciales.",
"Reading progress on this device differs from \"{{deviceName}}\".": "El progreso de lectura en este dispositivo difiere del de «{{deviceName}}».",
"This device": "Este dispositivo",
"Red": "Rojo",
"Yellow": "Amarillo",
"Green": "Verde",
"Blue": "Azul",
"Violet": "Violeta",
"Edit color": "Editar color",
"Add custom color": "Añadir color personalizado",
"Add label": "Añadir etiqueta",
"Choose color": "Elegir color",
"Connected to Hardcover. Last synced {{time}}.": "Conectado a Hardcover. Última sincronización {{time}}.",
"Integrations": "Integraciones",
"Hardcover": "Hardcover",
"Connected to Readwise. Last synced {{time}}.": "Conectado a Readwise. Última sincronización {{time}}.",
"Readwise": "Readwise",
"Browse and download books from online catalogs.": "Explora y descarga libros desde catálogos en línea.",
"Connected as {{user}}": "Conectado como {{user}}",
"Not connected": "No conectado",
"{{count}} catalog_one": "{{count}} catálogo",
"{{count}} catalog_many": "{{count}} catálogos",
"{{count}} catalog_other": "{{count}} catálogos",
"No catalogs": "Sin catálogos",
"Connect Readest to external services for sync, highlights, and catalogs.": "Conecta Readest con servicios externos para sincronización, resaltados y catálogos.",
"Reading Sync": "Sincronización de lectura",
"Content Sources": "Fuentes de contenido",
"Scroll tabs": "Desplazar pestañas",
"Catalog actions": "Acciones del catálogo",
"File Content": "Contenido del archivo",
"Start your library": "Empieza tu biblioteca",
"Pick a book from your device to add it to your library.": "Elige un libro de tu dispositivo para añadirlo a tu biblioteca.",
"Drop a book anywhere on this window, or pick one from your computer.": "Suelta un libro en cualquier parte de esta ventana o elige uno desde tu computadora.",
"Sign in to sync your library": "Inicia sesión para sincronizar tu biblioteca"
}
@@ -101,11 +101,9 @@
"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 پشتیبانی نمی‌شود",
"Override Book Font": "جایگزینی فونت کتاب",
"Apply to All Books": "اِعمال برای همه‌ی کتاب‌ها",
"Apply to This Book": "اِعمال برای این کتاب",
@@ -172,9 +170,6 @@
"Token": "توکن",
"Your OTP token": "کد یک‌بارمصرف شما",
"Verify token": "تأیید کد",
"Sign in with Google": "ورود با گوگل",
"Sign in with Apple": "ورود با اپل",
"Sign in with GitHub": "ورود با گیت‌هاب",
"Account": "حساب",
"Failed to delete user. Please try again later.": "حذف کاربر ناموفق بود. لطفاً بعداً دوباره تلاش کنید.",
"Community Support": "پشتیبانی جامعه",
@@ -187,7 +182,6 @@
"RTL Direction": "جهت راست‌به‌چپ",
"Maximum Column Height": "حداکثر ارتفاع ستون",
"Maximum Column Width": "حداکثر عرض ستون",
"Continuous Scroll": "پیمایش مداوم",
"Fullscreen": "تمام‌صفحه",
"No supported files found. Supported formats: {{formats}}": "فایل پشتیبانی‌شده‌ای یافت نشد. فرمت‌های قابل پشتیبانی: {{formats}}",
"Drop to Import Books": "برای وارد کردن کتاب‌ها، فایل را رها کنید",
@@ -303,7 +297,6 @@
"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}} دقیقه تا انتهای فصل",
@@ -337,11 +330,11 @@
"Fit": "متناسب",
"Reset {{settings}}": "بازنشانی {{settings}}",
"Reset Settings": "بازنشانی تنظیمات",
"{{count}} pages left in chapter_one": "{{count}} صفحه تا انتهای فصل",
"{{count}} pages left in chapter_other": "{{count}} صفحه تا انتهای فصل",
"{{count}} pages left in chapter_one": "<0>{{count}}</0><1> صفحه تا انتهای فصل</1>",
"{{count}} pages left in chapter_other": "<0>{{count}}</0><1> صفحه تا انتهای فصل</1>",
"Show Remaining Pages": "نمایش صفحات باقی‌مانده",
"Source Han Serif CN VF": "Source Han Serif CN VF",
"Huiwen-mincho": "Huiwen-mincho",
"Source Han Serif CN": "Source Han Serif CN",
"Huiwen-MinchoGBK": "Huiwen Mincho",
"KingHwa_OldSong": "KingHwa_OldSong",
"Manage Subscription": "مدیریت اشتراک",
"Coming Soon": "به‌زودی",
@@ -466,14 +459,12 @@
"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": "فقط دریافت تغییرات",
"Checksum Method": "روش Checksum",
"File Content (recommended)": "محتوای فایل (توصیه‌شده)",
"File Name": "نام فایل",
"Device Name": "نام دستگاه",
"Connect to your KOReader Sync server.": "به سرور همگام‌سازی KOReader خود متصل شوید.",
@@ -484,10 +475,7 @@
"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}}%)",
@@ -602,11 +590,10 @@
"Size": "اندازه",
"Cover": "جلد",
"Contain": "شامل",
"{{number}} pages left in chapter": "{{number}} صفحه تا انتهای فصل",
"{{number}} pages left in chapter": "<0>{{number}}</0><1> صفحه تا انتهای فصل</1>",
"Device": "دستگاه",
"E-Ink Mode": "حالت E-Ink",
"Highlight Colors": "رنگ‌های هایلایت",
"Auto Screen Brightness": "روشنایی خودکار صفحه",
"Pagination": "صفحه‌بندی",
"Disable Double Tap": "غیرفعال کردن کلیک دوبل",
"Tap to Paginate": "ضربه برای صفحه‌بندی",
@@ -641,7 +628,6 @@
"Strikethrough": "خط‌خورده",
"Squiggly": "خط‌موج‌دار",
"Outline": "خط‌کشی",
"Save Current Color": "ذخیره رنگ فعلی",
"Quick Colors": "رنگ‌های سریع",
"Highlighter": "ماژیک هایلایت",
"Save Book Cover": "ذخیره جلد کتاب",
@@ -699,9 +685,7 @@
"Validating...": "در حال بررسی...",
"View All": "مشاهده همه",
"Forward": "بعدی",
"OPDS Catalog": "فهرست OPDS",
"Home": "خانه",
"Library": "کتابخانه",
"{{count}} items_one": "{{count}} مورد",
"{{count}} items_other": "{{count}} مورد",
"Download completed": "دانلود کامل شد",
@@ -721,5 +705,712 @@
"Last": "آخرین",
"Cannot Load Page": "بارگذاری صفحه امکان‌پذیر نیست",
"An error occurred": "خطایی رخ داد",
"Online Library": "کتابخانه آنلاین"
"Online Library": "کتابخانه آنلاین",
"URL must start with http:// or https://": "آدرس باید با http:// یا https:// شروع شود",
"Title, Author, Tag, etc...": "عنوان، نویسنده، برچسب و غیره...",
"Query": "پرس‌وجو",
"Subject": "موضوع",
"Enter {{terms}}": "وارد کردن {{terms}}",
"No search results found": "هیچ نتیجه‌ای یافت نشد",
"Failed to load OPDS feed: {{status}} {{statusText}}": "بارگذاری فید OPDS ناموفق بود: {{status}} {{statusText}}",
"Search in {{title}}": "جستجو در {{title}}",
"Manage Storage": "مدیریت فضای ذخیره‌سازی",
"Failed to load files": "بارگیری فایل‌ها ناموفق بود",
"Deleted {{count}} file(s)_one": "{{count}} فایل حذف شد",
"Deleted {{count}} file(s)_other": "{{count}} فایل حذف شدند",
"Failed to delete {{count}} file(s)_one": "حذف {{count}} فایل ناموفق بود",
"Failed to delete {{count}} file(s)_other": "حذف {{count}} فایل ناموفق بود",
"Failed to delete files": "حذف فایل‌ها ناموفق بود",
"Total Files": "کل فایل‌ها",
"Total Size": "اندازه کل",
"Quota": "سهمیه",
"Used": "استفاده‌شده",
"Files": "فایل‌ها",
"Search files...": "جستجوی فایل‌ها...",
"Newest First": "جدیدترین‌ها",
"Oldest First": "قدیمی‌ترین‌ها",
"Largest First": "بزرگ‌ترین‌ها",
"Smallest First": "کوچک‌ترین‌ها",
"Name A-Z": "نام AZ",
"Name Z-A": "نام ZA",
"{{count}} selected_one": "{{count}} مورد انتخاب شد",
"{{count}} selected_other": "{{count}} مورد انتخاب شدند",
"Delete Selected": "حذف موارد انتخاب‌شده",
"Created": "ایجاد شده",
"No files found": "هیچ فایلی پیدا نشد",
"No files uploaded yet": "هنوز فایلی بارگذاری نشده است",
"files": "فایل‌ها",
"Page {{current}} of {{total}}": "صفحه {{current}} از {{total}}",
"Are you sure to delete {{count}} selected file(s)?_one": "آیا از حذف {{count}} فایل انتخاب‌شده مطمئن هستید؟",
"Are you sure to delete {{count}} selected file(s)?_other": "آیا از حذف {{count}} فایل انتخاب‌شده مطمئن هستید؟",
"Cloud Storage Usage": "استفاده از فضای ذخیره‌سازی ابری",
"Rename Group": "تغییر نام گروه",
"From Directory": "از مسیر",
"Successfully imported {{count}} book(s)_one": "با موفقیت 1 کتاب وارد شد",
"Successfully imported {{count}} book(s)_other": "با موفقیت {{count}} کتاب وارد شد",
"Count": "تعداد",
"Start Page": "صفحه شروع",
"Search in OPDS Catalog...": "جستجو در فهرست OPDS...",
"Please log in to use advanced TTS features": "لطفاً برای استفاده از ویژگی‌های پیشرفته TTS وارد شوید.",
"Word limit of 30 words exceeded.": "محدودیت ۳۰ واژه‌ای تجاوز شد.",
"Proofread": "بازبینی",
"Current selection": "انتخاب فعلی",
"All occurrences in this book": "همه موارد در این کتاب",
"All occurrences in your library": "همه موارد در کتابخانه شما",
"Selected text:": "متن انتخاب‌شده:",
"Replace with:": "جایگزین با:",
"Enter text...": "متن را وارد کنید…",
"Case sensitive:": "حساس به حروف بزرگ و کوچک:",
"Scope:": "دامنه:",
"Selection": "انتخاب",
"Library": "کتابخانه",
"Yes": "بله",
"No": "خیر",
"Proofread Replacement Rules": "قوانین جایگزینی بازبینی",
"Selected Text Rules": "قوانین متن انتخاب‌شده",
"No selected text replacement rules": "هیچ قانون جایگزینی برای متن انتخاب‌شده وجود ندارد",
"Book Specific Rules": "قوانین مخصوص کتاب",
"No book-level replacement rules": "هیچ قانون جایگزینی در سطح کتاب وجود ندارد",
"Disable Quick Action": "غیرفعال‌سازی اقدام سریع",
"Enable Quick Action on Selection": "فعال‌سازی اقدام سریع هنگام انتخاب",
"None": "هیچ‌کدام",
"Annotation Tools": "ابزارهای یادداشت‌گذاری",
"Enable Quick Actions": "فعال‌سازی اقدامات سریع",
"Quick Action": "اقدام سریع",
"Copy to Notebook": "کپی به دفترچه",
"Copy text after selection": "کپی متن پس از انتخاب",
"Highlight text after selection": "برجسته‌سازی متن پس از انتخاب",
"Annotate text after selection": "یادداشت‌گذاری متن پس از انتخاب",
"Search text after selection": "جستجوی متن پس از انتخاب",
"Look up text in dictionary after selection": "جستجوی متن در فرهنگ لغت پس از انتخاب",
"Translate text after selection": "ترجمه متن پس از انتخاب",
"Read text aloud after selection": "خواندن متن با صدای بلند پس از انتخاب",
"Proofread text after selection": "بازبینی متن پس از انتخاب",
"{{activeCount}} active, {{pendingCount}} pending": "{{activeCount}} فعال، {{pendingCount}} در انتظار",
"{{failedCount}} failed": "{{failedCount}} ناموفق",
"Waiting...": "در انتظار...",
"Failed": "ناموفق",
"Completed": "تکمیل شده",
"Cancelled": "لغو شده",
"Retry": "تلاش مجدد",
"Active": "فعال",
"Transfer Queue": "صف انتقال",
"Upload All": "بارگذاری همه",
"Download All": "دانلود همه",
"Resume Transfers": "ادامه انتقال‌ها",
"Pause Transfers": "توقف انتقال‌ها",
"Pending": "در انتظار",
"No transfers": "بدون انتقال",
"Retry All": "تلاش مجدد همه",
"Clear Completed": "پاک کردن تکمیل شده‌ها",
"Clear Failed": "پاک کردن ناموفق‌ها",
"Upload queued: {{title}}": "بارگذاری در صف: {{title}}",
"Download queued: {{title}}": "دانلود در صف: {{title}}",
"Book not found in library": "کتاب در کتابخانه یافت نشد",
"Unknown error": "خطای ناشناخته",
"Please log in to continue": "لطفاً برای ادامه وارد شوید",
"Cloud File Transfers": "انتقال فایل‌های ابری",
"Show Search Results": "نمایش نتایج جستجو",
"Search results for '{{term}}'": "نتایج برای «{{term}}»",
"Close Search": "بستن جستجو",
"Previous Result": "نتیجه قبلی",
"Next Result": "نتیجه بعدی",
"Bookmarks": "نشانک‌ها",
"Annotations": "یادداشت‌ها",
"Show Results": "نمایش نتایج",
"Clear search": "پاک کردن جستجو",
"Clear search history": "پاک کردن تاریخچه جستجو",
"Tap to Toggle Footer": "برای تغییر پاورقی ضربه بزنید",
"Show Current Time": "نمایش زمان فعلی",
"Use 24 Hour Clock": "استفاده از ساعت ۲۴ ساعته",
"Show Current Battery Status": "نمایش وضعیت فعلی باتری",
"Exported successfully": "صادرات موفق",
"Book exported successfully.": "کتاب با موفقیت صادر شد.",
"Failed to export the book.": "صادر کردن کتاب ناموفق بود.",
"Export Book": "صادر کردن کتاب",
"Whole word:": "کلمه کامل:",
"Date Published": "تاریخ انتشار",
"Only for TTS:": "فقط برای TTS:",
"Uploaded": "بارگذاری شد",
"Downloaded": "دانلود شد",
"Deleted": "حذف شد",
"Note:": "یادداشت:",
"Time:": "زمان:",
"Format Options": "گزینه‌های قالب",
"Export Date": "تاریخ خروجی",
"Chapter Titles": "عناوین فصل",
"Chapter Separator": "جداکننده فصل",
"Highlights": "برجسته‌ها",
"Note Date": "تاریخ یادداشت",
"Advanced": "پیشرفته",
"Hide": "پنهان کردن",
"Show": "نمایش",
"Use Custom Template": "استفاده از الگوی سفارشی",
"Export Template": "الگوی خروجی",
"Template Syntax:": "نحو الگو:",
"Insert value": "درج مقدار",
"Format date (locale)": "قالب‌بندی تاریخ (محلی)",
"Format date (custom)": "قالب‌بندی تاریخ (سفارشی)",
"Conditional": "شرطی",
"Loop": "حلقه",
"Available Variables:": "متغیرهای موجود:",
"Book title": "عنوان کتاب",
"Book author": "نویسنده کتاب",
"Export date": "تاریخ خروجی",
"Array of chapters": "آرایه فصل‌ها",
"Chapter title": "عنوان فصل",
"Array of annotations": "آرایه یادداشت‌ها",
"Highlighted text": "متن برجسته‌شده",
"Annotation note": "یادداشت حاشیه",
"Date Format Tokens:": "نشانه‌های قالب تاریخ:",
"Year (4 digits)": "سال (۴ رقم)",
"Month (01-12)": "ماه (۰۱-۱۲)",
"Day (01-31)": "روز (۰۱-۳۱)",
"Hour (00-23)": "ساعت (۰۰-۲۳)",
"Minute (00-59)": "دقیقه (۰۰-۵۹)",
"Second (00-59)": "ثانیه (۰۰-۵۹)",
"Show Source": "نمایش منبع",
"No content to preview": "محتوایی برای پیش‌نمایش وجود ندارد",
"Export": "خروجی",
"Set Timeout": "تنظیم مهلت زمانی",
"Select Voice": "انتخاب صدا",
"Toggle Sticky Bottom TTS Bar": "تغییر نوار TTS ثابت",
"Display what I'm reading on Discord": "نمایش کتاب در حال خواندن در Discord",
"Show on Discord": "نمایش در Discord",
"Instant {{action}}": "{{action}} فوری",
"Instant {{action}} Disabled": "{{action}} فوری غیرفعال شد",
"Annotation": "یادداشت",
"Reset Template": "بازنشانی قالب",
"Annotation style": "سبک یادداشت",
"Annotation color": "رنگ یادداشت",
"Annotation time": "زمان یادداشت",
"AI": "هوش مصنوعی",
"Are you sure you want to re-index this book?": "آیا مطمئن هستید که می‌خواهید این کتاب را دوباره فهرست کنید؟",
"Enable AI in Settings": "فعال کردن هوش مصنوعی در تنظیمات",
"Index This Book": "فهرست کردن این کتاب",
"Enable AI search and chat for this book": "فعال کردن جستجو و گفتگوی هوش مصنوعی برای این کتاب",
"Start Indexing": "شروع فهرست‌بندی",
"Indexing book...": "در حال فهرست‌بندی کتاب...",
"Preparing...": "در حال آماده‌سازی...",
"Delete this conversation?": "این مکالمه حذف شود؟",
"No conversations yet": "هنوز مکالمه‌ای وجود ندارد",
"Start a new chat to ask questions about this book": "یک گفتگوی جدید برای پرسیدن سؤال درباره این کتاب شروع کنید",
"Rename": "تغییر نام",
"New Chat": "گفتگوی جدید",
"Chat": "گفتگو",
"Please enter a model ID": "لطفاً شناسه مدل را وارد کنید",
"Model not available or invalid": "مدل در دسترس نیست یا نامعتبر است",
"Failed to validate model": "اعتبارسنجی مدل ناموفق بود",
"Couldn't connect to Ollama. Is it running?": "اتصال به Ollama ممکن نشد. آیا در حال اجرا است؟",
"Invalid API key or connection failed": "کلید API نامعتبر است یا اتصال ناموفق بود",
"Connection failed": "اتصال ناموفق بود",
"AI Assistant": "دستیار هوش مصنوعی",
"Enable AI Assistant": "فعال کردن دستیار هوش مصنوعی",
"Provider": "ارائه‌دهنده",
"Ollama (Local)": "Ollama (محلی)",
"AI Gateway (Cloud)": "دروازه هوش مصنوعی (ابری)",
"Ollama Configuration": "پیکربندی Ollama",
"Refresh Models": "بازخوانی مدل‌ها",
"AI Model": "مدل هوش مصنوعی",
"No models detected": "هیچ مدلی شناسایی نشد",
"AI Gateway Configuration": "پیکربندی دروازه هوش مصنوعی",
"Choose from a selection of high-quality, economical AI models. You can also bring your own model by selecting \"Custom Model\" below.": "از میان مجموعه‌ای از مدل‌های هوش مصنوعی با کیفیت و اقتصادی انتخاب کنید. همچنین می‌توانید با انتخاب \"مدل سفارشی\" در زیر، مدل خود را استفاده کنید.",
"API Key": "کلید API",
"Get Key": "دریافت کلید",
"Model": "مدل",
"Custom Model...": "مدل سفارشی...",
"Custom Model ID": "شناسه مدل سفارشی",
"Validate": "اعتبارسنجی",
"Model available": "مدل در دسترس است",
"Connection": "اتصال",
"Test Connection": "تست اتصال",
"Connected": "متصل",
"Custom Colors": "رنگ‌های سفارشی",
"Color E-Ink Mode": "حالت جوهر الکترونیک رنگی",
"Reading Ruler": "خط‌کش مطالعه",
"Enable Reading Ruler": "فعال‌سازی خط‌کش مطالعه",
"Lines to Highlight": "خطوط برای تمایز",
"Ruler Color": "رنگ خط‌کش",
"Command Palette": "پالت فرمان",
"Search settings and actions...": "جستجوی تنظیمات و عملیات...",
"No results found for": "نتیجه‌ای برای این مورد یافت نشد",
"Type to search settings and actions": "برای جستجوی تنظیمات و عملیات تایپ کنید",
"Recent": "اخیر",
"navigate": "ناوبری",
"select": "انتخاب",
"close": "بستن",
"Search Settings": "جستجوی تنظیمات",
"Page Margins": "حاشیه‌های صفحه",
"AI Provider": "ارائه دهنده هوش مصنوعی",
"Ollama URL": "آدرس Ollama",
"Ollama Model": "مدل Ollama",
"AI Gateway Model": "مدل AI Gateway",
"Actions": "عملیات",
"Navigation": "ناوبری",
"Set status for {{count}} book(s)_one": "تعیین وضعیت برای {{count}} کتاب",
"Set status for {{count}} book(s)_other": "تعیین وضعیت برای {{count}} کتاب",
"Mark as Unread": "علامت‌گذاری به‌عنوان خوانده‌نشده",
"Mark as Finished": "علامت‌گذاری به‌عنوان تمام‌شده",
"Finished": "تمام‌شده",
"Unread": "خوانده‌نشده",
"Clear Status": "پاک کردن وضعیت",
"Status": "وضعیت",
"Loading": "در حال بارگذاری...",
"Exit Paragraph Mode": "خروج از حالت بند",
"Paragraph Mode": "حالت بند",
"Embedding Model": "مدل جاسازی",
"{{count}} book(s) synced_one": "{{count}} کتاب همگام شد",
"{{count}} book(s) synced_other": "{{count}} کتاب همگام شدند",
"Unable to start RSVP": "قادر به شروع RSVP نیست",
"RSVP not supported for PDF": "RSVP برای PDF پشتیبانی نمی‌شود",
"Select Chapter": "انتخاب فصل",
"Context": "زمینه",
"Ready": "آماده",
"Chapter Progress": "پیشرفت فصل",
"words": "کلمات",
"{{time}} left": "{{time}} باقی‌مانده",
"Reading progress": "پیشرفت مطالعه",
"Skip back 15 words": "۱۵ کلمه به عقب",
"Back 15 words (Shift+Left)": "۱۵ کلمه به عقب (Shift+Left)",
"Pause (Space)": "توقف (Space)",
"Play (Space)": "پخش (Space)",
"Skip forward 15 words": "۱۵ کلمه به جلو",
"Forward 15 words (Shift+Right)": "۱۵ کلمه به جلو (Shift+Right)",
"Decrease speed": "کاهش سرعت",
"Slower (Left/Down)": "کندتر (Left/Down)",
"Increase speed": "افزایش سرعت",
"Faster (Right/Up)": "سریع‌تر (Right/Up)",
"Start RSVP Reading": "شروع مطالعه RSVP",
"Choose where to start reading": "انتخاب کنید از کجا مطالعه شروع شود",
"From Chapter Start": "از ابتدای فصل",
"Start reading from the beginning of the chapter": "شروع مطالعه از ابتدای فصل",
"Resume": "ادامه",
"Continue from where you left off": "ادامه از جایی که متوقف شدید",
"From Current Page": "از صفحه فعلی",
"Start from where you are currently reading": "شروع از جایی که در حال مطالعه هستید",
"From Selection": "از انتخاب",
"Speed Reading Mode": "حالت مطالعه سریع",
"Scroll left": "اسکرول به چپ",
"Scroll right": "اسکرول به راست",
"Library Sync Progress": "پیشرفت همگام‌سازی کتابخانه",
"Back to library": "بازگشت به کتابخانه",
"Group by...": "گروه‌بندی بر اساس...",
"Export as Plain Text": "خروجی به صورت متن ساده",
"Export as Markdown": "خروجی به صورت Markdown",
"Show Page Navigation Buttons": "دکمه‌های ناوبری",
"Page {{number}}": "صفحه {{number}}",
"highlight": "هایلایت",
"underline": "زیرخط",
"squiggly": "موج‌دار",
"red": "قرمز",
"violet": "بنفش",
"blue": "آبی",
"green": "سبز",
"yellow": "زرد",
"Select {{style}} style": "انتخاب سبک {{style}}",
"Select {{color}} color": "انتخاب رنگ {{color}}",
"Close Book": "بستن کتاب",
"Speed Reading": "تندخوانی",
"Close Speed Reading": "بستن تندخوانی",
"Authors": "نویسندگان",
"Books": "کتاب‌ها",
"Groups": "گروه‌ها",
"Back to TTS Location": "بازگشت به مکان TTS",
"Metadata": "فراداده",
"Image viewer": "نمایشگر تصویر",
"Previous Image": "تصویر قبلی",
"Next Image": "تصویر بعدی",
"Zoomed": "بزرگنمایی شده",
"Zoom level": "سطح بزرگنمایی",
"Table viewer": "نمایشگر جدول",
"Unable to connect to Readwise. Please check your network connection.": "اتصال به Readwise امکان‌پذیر نیست. لطفاً اتصال شبکه خود را بررسی کنید.",
"Invalid Readwise access token": "توکن دسترسی Readwise نامعتبر است",
"Disconnected from Readwise": "اتصال از Readwise قطع شد",
"Never": "هرگز",
"Sync Enabled": "همگام‌سازی فعال شد",
"Disconnect": "قطع اتصال",
"Connect your Readwise account to sync highlights.": "برای همگام‌سازی هایلایت‌ها، حساب Readwise خود را متصل کنید.",
"Get your access token at": "توکن دسترسی خود را از اینجا دریافت کنید:",
"Access Token": "توکن دسترسی",
"Paste your Readwise access token": "توکن دسترسی Readwise خود را جای‌گذاری کنید",
"Readwise Sync": "همگام‌سازی Readwise",
"Push Highlights": "ارسال هایلایت‌ها",
"Highlights synced to Readwise": "هایلایت‌ها با Readwise همگام‌سازی شدند",
"Readwise sync failed: no internet connection": "همگام‌سازی Readwise ناموفق بود: اتصال اینترنت برقرار نیست",
"Readwise sync failed: {{error}}": "همگام‌سازی Readwise ناموفق بود: {{error}}",
"System Screen Brightness": "روشنایی صفحه نمایش سیستم",
"Page:": "صفحه:",
"Page: {{number}}": "صفحه: {{number}}",
"Annotation page number": "شماره صفحه یادداشت",
"Translating...": "در حال ترجمه...",
"Show Battery Percentage": "نمایش درصد باتری",
"Hide Scrollbar": "مخفی کردن نوار پیمایش",
"Skip to last reading position": "رفتن به آخرین موقعیت مطالعه",
"Show context": "نمایش زمینه",
"Hide context": "پنهان کردن زمینه",
"Decrease font size": "کاهش اندازه قلم",
"Increase font size": "افزایش اندازه قلم",
"Focus": "تمرکز",
"Theme color": "رنگ پوسته",
"Import failed": "وارد کردن ناموفق بود",
"Available Formatters:": "قالب‌بندهای موجود:",
"Format date": "قالب‌بندی تاریخ",
"Markdown block quote (> per line)": "نقل‌قول Markdown (> در هر خط)",
"Newlines to <br>": "خطوط جدید به <br>",
"Change case": "تغییر بزرگی/کوچکی حروف",
"Trim whitespace": "حذف فاصله‌ها",
"Truncate to n characters": "کوتاه کردن به n نویسه",
"Replace text": "جایگزینی متن",
"Fallback value": "مقدار پیش‌فرض",
"Get length": "دریافت طول",
"First/last element": "عنصر اول/آخر",
"Join array": "پیوستن آرایه",
"Backup failed: {{error}}": "پشتیبان‌گیری ناموفق: {{error}}",
"Select Backup": "انتخاب پشتیبان",
"Restore failed: {{error}}": "بازیابی ناموفق: {{error}}",
"Backup & Restore": "پشتیبان‌گیری و بازیابی",
"Create a backup of your library or restore from a previous backup. Restoring will merge with your current library.": "از کتابخانه خود پشتیبان بگیرید یا از پشتیبان قبلی بازیابی کنید. بازیابی با کتابخانه فعلی شما ادغام خواهد شد.",
"Backup Library": "پشتیبان‌گیری کتابخانه",
"Restore Library": "بازیابی کتابخانه",
"Creating backup...": "در حال ایجاد پشتیبان...",
"Restoring library...": "در حال بازیابی کتابخانه...",
"{{current}} of {{total}} items": "{{current}} از {{total}} مورد",
"Backup completed successfully!": "پشتیبان‌گیری با موفقیت انجام شد!",
"Restore completed successfully!": "بازیابی با موفقیت انجام شد!",
"Your library has been saved to the selected location.": "کتابخانه شما در مکان انتخاب‌شده ذخیره شد.",
"{{added}} books added, {{updated}} books updated.": "{{added}} کتاب اضافه شد، {{updated}} کتاب به‌روزرسانی شد.",
"Operation failed": "عملیات ناموفق",
"Loading library...": "در حال بارگذاری کتابخانه...",
"{{count}} books refreshed_one": "{{count}} کتاب به‌روزرسانی شد",
"{{count}} books refreshed_other": "{{count}} کتاب به‌روزرسانی شد",
"Failed to refresh metadata": "به‌روزرسانی فراداده ناموفق",
"Refresh Metadata": "به‌روزرسانی فراداده",
"Keyboard Shortcuts": "میانبرهای صفحه‌کلید",
"View all keyboard shortcuts": "مشاهده همه میانبرهای صفحه‌کلید",
"Switch Sidebar Tab": "تغییر زبانه نوار کناری",
"Toggle Notebook": "نمایش/پنهان کردن دفترچه یادداشت",
"Search in Book": "جستجو در کتاب",
"Toggle Scroll Mode": "تغییر حالت پیمایش",
"Toggle Select Mode": "تغییر حالت انتخاب",
"Toggle Bookmark": "تغییر نشانک",
"Toggle Text to Speech": "تغییر متن به گفتار",
"Play / Pause TTS": "پخش / توقف TTS",
"Toggle Paragraph Mode": "تغییر حالت پاراگراف",
"Highlight Selection": "برجسته‌سازی انتخاب",
"Underline Selection": "زیرخط انتخاب",
"Annotate Selection": "افزودن یادداشت به انتخاب",
"Search Selection": "جستجوی انتخاب",
"Copy Selection": "کپی انتخاب",
"Translate Selection": "ترجمه انتخاب",
"Dictionary Lookup": "جستجو در فرهنگ لغت",
"Read Aloud Selection": "خواندن بلند انتخاب",
"Proofread Selection": "بازبینی انتخاب",
"Open Settings": "باز کردن تنظیمات",
"Open Command Palette": "باز کردن پالت فرمان",
"Show Keyboard Shortcuts": "نمایش میانبرهای صفحه‌کلید",
"Open Books": "باز کردن کتاب‌ها",
"Toggle Fullscreen": "تغییر تمام‌صفحه",
"Close Window": "بستن پنجره",
"Quit App": "خروج از برنامه",
"Go Left / Previous Page": "رفتن به چپ / صفحه قبلی",
"Go Right / Next Page": "رفتن به راست / صفحه بعدی",
"Go Up": "رفتن به بالا",
"Go Down": "رفتن به پایین",
"Previous Chapter": "فصل قبلی",
"Next Chapter": "فصل بعدی",
"Scroll Half Page Down": "پیمایش نیم صفحه پایین",
"Scroll Half Page Up": "پیمایش نیم صفحه بالا",
"Save Note": "ذخیره یادداشت",
"Single Section Scroll": "پیمایش تک‌بخشی",
"General": "عمومی",
"Text to Speech": "تبدیل متن به گفتار",
"Zoom": "بزرگ‌نمایی",
"Window": "پنجره",
"Your Bookshelf": "قفسه کتاب شما",
"TTS": "تبدیل متن به گفتار",
"Media Info": "اطلاعات رسانه",
"Update Frequency": "تناوب به‌روزرسانی",
"Every Sentence": "هر جمله",
"Every Paragraph": "هر پاراگراف",
"Every Chapter": "هر فصل",
"TTS Media Info Update Frequency": "تناوب به‌روزرسانی اطلاعات رسانه TTS",
"Select reading speed": "انتخاب سرعت خواندن",
"Drag to seek": "بکشید برای جستجو",
"Punctuation Delay": "تأخیر نشانه‌گذاری",
"Please confirm that this OPDS connection will be proxied through Readest servers on the web app before continuing.": "لطفاً تأیید کنید که این اتصال OPDS از طریق سرورهای Readest در برنامه وب پروکسی خواهد شد.",
"Custom Headers (optional)": "سرآیندهای سفارشی (اختیاری)",
"Add one header per line using \"Header-Name: value\".": "در هر خط یک سرآیند با استفاده از \"Header-Name: value\" اضافه کنید.",
"I understand this OPDS connection will be proxied through Readest servers on the web app. If I do not trust Readest with these credentials or headers, I should use the native app instead.": "متوجه هستم که این اتصال OPDS از طریق سرورهای Readest در برنامه وب پروکسی خواهد شد. اگر به Readest برای این اعتبارنامه‌ها یا سرآیندها اعتماد ندارم، باید از برنامه بومی استفاده کنم.",
"Unable to connect to Hardcover. Please check your network connection.": "اتصال به Hardcover ممکن نیست. لطفاً اتصال شبکه خود را بررسی کنید.",
"Invalid Hardcover API token": "توکن API نامعتبر Hardcover",
"Disconnected from Hardcover": "قطع اتصال از Hardcover",
"Connect your Hardcover account to sync reading progress and notes.": "حساب Hardcover خود را برای همگام‌سازی پیشرفت مطالعه و یادداشت‌ها متصل کنید.",
"Get your API token from hardcover.app → Settings → API.": "توکن API خود را از hardcover.app ← تنظیمات ← API دریافت کنید.",
"API Token": "توکن API",
"Paste your Hardcover API token": "توکن API Hardcover خود را جای‌گذاری کنید",
"Hardcover Sync": "همگام‌سازی Hardcover",
"Push Notes": "ارسال یادداشت‌ها",
"No annotations or excerpts to sync for this book.": "هیچ حاشیه‌نویسی یا گزیده‌ای برای همگام‌سازی این کتاب وجود ندارد.",
"Configure Hardcover in Settings first.": "ابتدا Hardcover را در تنظیمات پیکربندی کنید.",
"No new Hardcover note changes to sync.": "تغییرات جدیدی در یادداشت‌های Hardcover برای همگام‌سازی وجود ندارد.",
"Hardcover synced: {{inserted}} new, {{updated}} updated, {{skipped}} unchanged": "Hardcover همگام‌سازی شد: {{inserted}} جدید، {{updated}} به‌روزرسانی، {{skipped}} بدون تغییر",
"Hardcover notes sync failed: {{error}}": "همگام‌سازی یادداشت‌های Hardcover ناموفق: {{error}}",
"Reading progress synced to Hardcover": "پیشرفت مطالعه با Hardcover همگام‌سازی شد",
"Hardcover progress sync failed: {{error}}": "همگام‌سازی پیشرفت Hardcover ناموفق: {{error}}",
"ISBN": "ISBN",
"Keep existing source identifier": "حفظ شناسه منبع موجود",
"Toggle Toolbar": "تغییر نوار ابزار",
"Split Hyphens": "تقسیم خط‌تیره‌ها",
"Apply Theme Colors to PDF": "اعمال رنگ‌های پوسته روی PDF",
"Name": "نام",
"Remove": "حذف",
"Edit OPDS Catalog": "ویرایش کاتالوگ OPDS",
"Save Changes": "ذخیره تغییرات",
"Use Book Layout": "استفاده از چیدمان کتاب",
"End of this section. Continue to the next.": "پایان این بخش. به بخش بعدی ادامه دهید.",
"Auto-download": "دانلود خودکار",
"Auto-download new items": "دانلود خودکار موارد جدید",
"Automatically download new publications when the app syncs": "دانلود خودکار انتشارات جدید هنگام همگام‌سازی برنامه",
"{{count}} new item(s) downloaded from OPDS_one": "{{count}} مورد جدید از OPDS دانلود شد",
"{{count}} new item(s) downloaded from OPDS_other": "{{count}} مورد جدید از OPDS دانلود شدند",
"Failed to sync {{count}} OPDS catalog(s)_one": "همگام‌سازی {{count}} کاتالوگ OPDS ناموفق بود",
"Failed to sync {{count}} OPDS catalog(s)_other": "همگام‌سازی {{count}} کاتالوگ OPDS ناموفق بود",
"Last synced {{when}}": "آخرین همگام‌سازی {{when}}",
"Failed downloads": "دانلودهای ناموفق",
"No failed downloads": "هیچ دانلود ناموفقی وجود ندارد",
"Attempts: {{count}}_one": "تلاش‌ها: {{count}}",
"Attempts: {{count}}_other": "تلاش‌ها: {{count}}",
"Skip": "رد کردن",
"Skip all": "رد کردن همه",
"Retry all": "تلاش مجدد همه",
"{{count}} failed_one": "{{count}} ناموفق",
"{{count}} failed_other": "{{count}} ناموفق",
"(none)": "(هیچ‌کدام)",
"Identifiers": "شناسه‌ها",
"Read (Stream)": "خواندن (پخش)",
"Failed to start stream": "شروع پخش ناموفق بود",
"No dictionaries enabled": "هیچ فرهنگ‌لغتی فعال نیست",
"Enable a dictionary in Settings → Language → Dictionaries.": "فرهنگ‌لغتی را از تنظیمات ← زبان ← فرهنگ‌لغت‌ها فعال کنید.",
"Wiktionary": "ویکی‌واژه",
"Drag to reorder": "برای تغییر ترتیب بکشید",
"Built-in": "داخلی",
"Bundle is missing on this device. Re-import to use it.": "بسته در این دستگاه موجود نیست. برای استفاده دوباره وارد کنید.",
"This dictionary format is not supported.": "این قالب فرهنگ‌لغت پشتیبانی نمی‌شود.",
"MDict": "MDict",
"StarDict": "StarDict",
"Imported {{count}} dictionary_one": "{{count}} فرهنگ‌لغت وارد شد",
"Imported {{count}} dictionary_other": "{{count}} فرهنگ‌لغت وارد شد",
"Skipped incomplete bundles: {{names}}": "بسته‌های ناقص رد شدند: {{names}}",
"Failed to import dictionary: {{message}}": "وارد کردن فرهنگ‌لغت ناموفق بود: {{message}}",
"Dictionaries": "فرهنگ‌لغت‌ها",
"Delete Dictionary": "حذف فرهنگ‌لغت",
"Importing…": "در حال وارد کردن…",
"Import Dictionary": "وارد کردن فرهنگ‌لغت",
"No dictionaries available.": "هیچ فرهنگ‌لغتی در دسترس نیست.",
"StarDict bundles need .ifo, .idx, and .dict.dz files (.syn optional).": "بسته‌های StarDict به فایل‌های .ifo، .idx و .dict.dz نیاز دارند (.syn اختیاری).",
"Select all the bundle files together when importing.": "هنگام وارد کردن، همه فایل‌های بسته را با هم انتخاب کنید.",
"Manage Dictionaries": "مدیریت فرهنگ‌لغت‌ها",
"Select Dictionary Files": "انتخاب فایل‌های فرهنگ‌لغت",
"Read on Wikipedia →": "در ویکی‌پدیا بخوانید →",
"This link can't be opened": "این پیوند باز نمی‌شود",
"The annotation link is missing required information. The original link may have been truncated.": "اطلاعات مورد نیاز در پیوند یادداشت وجود ندارد. ممکن است پیوند اصلی کوتاه شده باشد.",
"Go to Readest": "رفتن به Readest",
"Open-source ebook reader for everyone, on every device.": "کتاب‌خوان الکترونیکی متن‌باز برای همه، روی هر دستگاهی.",
"Open in Readest": "باز کردن در Readest",
"If Readest didn't open automatically, choose an option below:": "اگر Readest به‌طور خودکار باز نشد، یکی از گزینه‌های زیر را انتخاب کنید:",
"Continue reading where you left off.": "از همان‌جا که رها کردید ادامه بدهید.",
"Readest logo": "نشان Readest",
"Opening Readest...": "در حال باز کردن Readest…",
"Open in Readest app": "باز کردن در برنامهٔ Readest",
"Continue in browser": "ادامه در مرورگر",
"Don't have Readest?": "Readest ندارید؟",
"Book not in your library": "این کتاب در کتابخانهٔ شما نیست",
"Share Book": "اشتراک‌گذاری کتاب",
"Could not create share link": "ایجاد لینک اشتراک‌گذاری امکان‌پذیر نبود",
"Link copied": "لینک کپی شد",
"Could not copy link": "کپی کردن لینک امکان‌پذیر نبود",
"Share revoked": "اشتراک‌گذاری لغو شد",
"Could not revoke share": "لغو اشتراک‌گذاری امکان‌پذیر نبود",
"Uploading book…": "در حال آپلود کتاب…",
"Generating…": "در حال ایجاد…",
"Generate share link": "ایجاد لینک اشتراک‌گذاری",
"Share URL": "آدرس اشتراک‌گذاری",
"Copy link": "کپی لینک",
"Copied": "کپی شد",
"Share via…": "اشتراک‌گذاری از طریق…",
"Expires {{date}}": "منقضی می‌شود در {{date}}",
"Revoking…": "در حال لغو…",
"Revoke share": "لغو اشتراک‌گذاری",
"Sign in to share books": "برای اشتراک‌گذاری کتاب‌ها وارد شوید",
"Expiring soon": "به زودی منقضی می‌شود",
"Missing share token": "توکن اشتراک‌گذاری وجود ندارد",
"Could not add to your library": "افزودن به کتابخانه شما امکان‌پذیر نبود",
"This share link is no longer available": "این لینک اشتراک‌گذاری دیگر در دسترس نیست",
"The original link may have expired or been revoked.": "لینک اصلی ممکن است منقضی شده یا لغو شده باشد.",
"Get Readest": "دریافت Readest",
"Loading shared book…": "در حال بارگذاری کتاب اشتراک‌گذاری شده…",
"Adding…": "در حال افزودن…",
"Add to my library": "افزودن به کتابخانه من",
"Could not load your shares": "بارگذاری اشتراک‌گذاری‌های شما امکان‌پذیر نبود",
"Revoked": "لغو شده",
"Expired": "منقضی شده",
"Shared books": "کتاب‌های اشتراک‌گذاری شده",
"You haven't shared any books yet": "هنوز کتابی به اشتراک نگذاشته‌اید",
"Open a book and tap Share to send it to a friend.": "یک کتاب باز کنید و روی اشتراک‌گذاری ضربه بزنید تا برای دوستتان بفرستید.",
"{{count}} active_one": "{{count}} فعال",
"{{count}} active_other": "{{count}} فعال",
"{{count}} downloads_one": "{{count}} دانلود",
"{{count}} downloads_other": "{{count}} دانلود",
"starts at saved page": "از صفحه ذخیره‌شده شروع می‌شود",
"More actions": "اقدامات بیشتر",
"Loading…": "در حال بارگذاری…",
"Load more": "بارگذاری بیشتر",
"Could not load shared book": "بارگذاری کتاب اشتراک‌گذاری شده امکان‌پذیر نبود",
"The share link is missing required information.": "لینک اشتراک‌گذاری اطلاعات لازم را ندارد.",
"Please check your connection and try again.": "لطفاً اتصال خود را بررسی کرده و دوباره تلاش کنید.",
"Sign in to import shared books": "برای وارد کردن کتاب‌های اشتراک‌گذاری شده وارد شوید",
"Already in your library": "از قبل در کتابخانه شما موجود است",
"Added to your library": "به کتابخانه شما افزوده شد",
"Could not import shared book": "وارد کردن کتاب اشتراک‌گذاری شده امکان‌پذیر نبود",
"Manage Shared Links": "مدیریت لینک‌های اشتراک‌گذاری",
"Expires in": "منقضی می‌شود در",
"{{count}} days_one": "۱ روز",
"{{count}} days_other": "{{count}} روز",
"Expires in {{count}} days_one": "در ۱ روز منقضی می‌شود",
"Expires in {{count}} days_other": "در {{count}} روز منقضی می‌شود",
"Expires in {{count}} hours_one": "در ۱ ساعت منقضی می‌شود",
"Expires in {{count}} hours_other": "در {{count}} ساعت منقضی می‌شود",
"Open in app": "باز کردن در برنامه",
"Shared with you": "با شما به اشتراک گذاشته شده",
"Downloading… {{percent}}%": "در حال دانلود… {{percent}}٪",
"Import progress": "پیشرفت وارد کردن",
"Share reading progress": "اشتراک‌گذاری پیشرفت مطالعه",
"Includes your reading progress": "شامل پیشرفت مطالعهٔ شماست",
"Synced {{time}}": "{{time}} همگام‌سازی شد",
"Sync Info": "اطلاعات همگام‌سازی",
"Last Synced": "آخرین همگام‌سازی",
"DICT": "DICT",
"Slob": "Slob",
"DICT bundles need a .index file and a .dict.dz file.": "بسته‌های DICT به یک فایل ‎.index‎ و یک فایل ‎.dict.dz‎ نیاز دارند.",
"Slob bundles need a .slob file.": "بسته‌های Slob به یک فایل ‎.slob‎ نیاز دارند.",
"URL template must start with http(s):// and contain %WORD%.": "الگوی URL باید با http(s):// شروع شود و حاوی %WORD% باشد.",
"Web": "وب",
"Add Web Search": "افزودن جست‌وجوی وب",
"Edit Web Search": "ویرایش جست‌وجوی وب",
"e.g. Google": "مثلاً Google",
"URL Template": "الگوی URL",
"Use %WORD% where the looked-up word should appear.": "از %WORD% در جایی که کلمه‌ی مورد جست‌وجو باید ظاهر شود، استفاده کنید.",
"Open the search result in your browser:": "نتیجه‌ی جست‌وجو را در مرورگر باز کنید:",
"Open in {{name}}": "بازکردن در {{name}}",
"Book Fingerprint": "اثر انگشت کتاب",
"Search the web": "جستجو در وب",
"App deeplink (readest://)": "پیوند درون‌برنامه‌ای (readest://)",
"Universal web link (https://)": "پیوند وب جهانی (https://)",
"Name cannot be empty.": "نام نمی‌تواند خالی باشد.",
"Replaced {{count}} existing dictionary_one": "{{count}} فرهنگ‌نامه موجود جایگزین شد",
"Replaced {{count}} existing dictionary_other": "{{count}} فرهنگ‌نامه موجود جایگزین شدند",
"Cancel Edit": "لغو ویرایش",
"Edit Dictionary": "ویرایش فرهنگ‌نامه",
"MDict bundles use .mdx files; companion .mdd and .css files are optional.": "بسته‌های MDict از فایل‌های .mdx استفاده می‌کنند؛ فایل‌های همراه .mdd و .css اختیاری هستند.",
"Dictionary name": "نام فرهنگ‌نامه",
"This audio can't play here — the dictionary uses an outdated format. Try one with Opus, MP3, or WAV audio.": "این صدا اینجا قابل پخش نیست — فرهنگ‌نامه از قالبی قدیمی استفاده می‌کند. فرهنگ‌نامه‌ای با صدای Opus، MP3 یا WAV را امتحان کنید.",
"File uploaded: {{title}}": "فایل «{{title}}» آپلود شد.",
"File downloaded: {{title}}": "فایل «{{title}}» دانلود شد.",
"Deleted cloud copy of the file: {{title}}": "نسخه‌ی ابری فایل «{{title}}» حذف شد.",
"Failed to upload file: {{title}}": "آپلود فایل «{{title}}» ناموفق بود.",
"Failed to download file: {{title}}": "دانلود فایل «{{title}}» ناموفق بود.",
"Failed to delete cloud copy of the file: {{title}}": "حذف نسخه‌ی ابری فایل «{{title}}» ناموفق بود.",
"{{percentage}}% used": "{{percentage}}٪ استفاده شده",
"Set PIN…": "تنظیم PIN…",
"Require a 4-digit PIN to open Readest": "برای باز کردن Readest یک PIN چهار رقمی الزامی شود",
"Change PIN…": "تغییر PIN…",
"Disable PIN…": "غیرفعال کردن PIN…",
"Sync passphrase ready": "عبارت عبور همگام‌سازی آماده است",
"This permanently deletes the encrypted credentials we sync (e.g., OPDS catalog passwords) on every device. Local copies are preserved. You will need to re-enter the sync passphrase or set a new one. Continue?": "این به طور دائم اعتبارنامه‌های رمزگذاری‌شده‌ای را که همگام‌سازی می‌کنیم (مانند رمزهای کاتالوگ OPDS) در همه دستگاه‌ها حذف می‌کند. نسخه‌های محلی حفظ می‌شوند. باید عبارت عبور همگام‌سازی را دوباره وارد کنید یا یک عبارت جدید تنظیم کنید. ادامه می‌دهید؟",
"Sync passphrase forgotten — all encrypted fields cleared": "عبارت عبور همگام‌سازی فراموش شد — تمام فیلدهای رمزگذاری‌شده پاک شدند",
"Sync passphrase": "عبارت عبور همگام‌سازی",
"Set passphrase": "تنظیم عبارت عبور",
"Forgot passphrase": "عبارت عبور را فراموش کرده‌اید",
"Incorrect PIN": "PIN نادرست",
"App locked": "برنامه قفل است",
"Enter your PIN": "PIN خود را وارد کنید",
"Readest is locked. Enter your 4-digit PIN to continue.": "Readest قفل است. برای ادامه، PIN چهار رقمی خود را وارد کنید.",
"PIN code": "کد PIN",
"Forgetting your PIN locks you out of this device. You'll need to clear the app's data to reset it.": "فراموش کردن PIN باعث می‌شود از این دستگاه خارج شوید. برای بازنشانی آن باید داده‌های برنامه را پاک کنید.",
"Passphrase must be at least 8 characters": "عبارت عبور باید حداقل ۸ نویسه باشد",
"Passphrases do not match": "عبارت‌های عبور مطابقت ندارند",
"Set sync passphrase": "تنظیم عبارت عبور همگام‌سازی",
"Enter sync passphrase": "عبارت عبور همگام‌سازی را وارد کنید",
"A sync passphrase encrypts your sensitive fields (like OPDS catalog credentials) before they sync. We never see this passphrase. Pick something memorable — there is no recovery without it.": "عبارت عبور همگام‌سازی، فیلدهای حساس شما (مانند اعتبارنامه‌های کاتالوگ OPDS) را قبل از همگام‌سازی رمزگذاری می‌کند. ما هرگز این عبارت عبور را نمی‌بینیم. چیزی به‌یادماندنی انتخاب کنید — بدون آن هیچ راه بازیابی وجود ندارد.",
"Enter the sync passphrase you set on another device to decrypt your synced credentials.": "برای رمزگشایی اعتبارنامه‌های همگام‌سازی‌شده، عبارت عبور همگام‌سازی که در دستگاه دیگری تنظیم کرده‌اید را وارد کنید.",
"Confirm passphrase": "تأیید عبارت عبور",
"Unlock": "باز کردن",
"Set PIN": "تنظیم PIN",
"Change PIN": "تغییر PIN",
"Disable PIN": "غیرفعال کردن PIN",
"Enter your current PIN, then choose a new 4-digit PIN.": "PIN فعلی خود را وارد کنید و سپس یک PIN چهار رقمی جدید انتخاب کنید.",
"Enter your current PIN to disable the app lock.": "برای غیرفعال کردن قفل برنامه، PIN فعلی خود را وارد کنید.",
"PIN must be {{length}} digits": "PIN باید {{length}} رقم باشد",
"PINs do not match": "PINها مطابقت ندارند",
"Current PIN": "PIN فعلی",
"New PIN": "PIN جدید",
"Confirm new PIN": "تأیید PIN جدید",
"Manage Sync": "مدیریت همگام‌سازی",
"Imported book files and library metadata": "فایل‌های کتاب وارد شده و فراداده کتابخانه",
"Last-read position, bookmarks, and per-book preferences": "آخرین موقعیت مطالعه، نشانک‌ها و تنظیمات هر کتاب",
"Highlights and notes": "هایلایت‌ها و یادداشت‌ها",
"Fonts": "قلم‌ها",
"Custom font files": "فایل‌های قلم سفارشی",
"Backgrounds": "پس‌زمینه‌ها",
"Custom background textures": "بافت‌های پس‌زمینه سفارشی",
"OPDS catalogs": "کاتالوگ‌های OPDS",
"Saved catalog URLs and (encrypted) credentials": "نشانی‌های ذخیره‌شده کاتالوگ و اعتبارنامه‌های (رمزنگاری‌شده)",
"Wrong sync passphrase — synced credentials could not be decrypted": "عبارت عبور همگام‌سازی نادرست است — رمزگشایی اعتبارنامه‌های همگام‌شده ممکن نیست",
"Sync passphrase data on the server was reset. Re-encrypting your credentials under the new passphrase…": "داده‌های عبارت عبور همگام‌سازی روی سرور بازنشانی شد. در حال رمزنگاری مجدد اعتبارنامه‌ها با عبارت عبور جدید…",
"Failed to decrypt synced credentials": "رمزگشایی اعتبارنامه‌های همگام‌شده ناموفق بود",
"Imported dictionary bundles and settings": "بسته‌های فرهنگ‌لغت وارد شده و تنظیمات",
"Choose what syncs across your devices. Disabling a category stops this device from sending or receiving rows of that kind. Anything already on the server is left alone, re-enabling resumes from where you stopped.": "انتخاب کنید چه چیزی بین دستگاه‌های شما همگام‌سازی شود. غیرفعال کردن یک دسته باعث می‌شود این دستگاه ارسال یا دریافت ردیف‌های آن نوع را متوقف کند. آنچه از قبل روی سرور است دست‌نخورده باقی می‌ماند، و با فعال‌سازی مجدد، از همان جایی که متوقف شده‌اید ادامه می‌یابد.",
"Data Sync": "همگام‌سازی داده‌ها",
"Manage Fonts": "مدیریت قلم‌ها",
"App settings": "تنظیمات برنامه",
"Theme, highlight colours, integrations (KOSync, Readwise, Hardcover), and dictionary order": "پوسته، رنگ‌های هایلایت، یکپارچه‌سازی‌ها (KOSync، Readwise، Hardcover) و ترتیب فرهنگ‌لغت‌ها",
"Required while Dictionaries sync is enabled": "هنگام فعال‌بودن همگام‌سازی فرهنگ‌لغت‌ها لازم است",
"Resets in {{hours}} hr {{minutes}} min": "تنظیم مجدد در {{hours}} ساعت {{minutes}} دقیقه",
"Pick a 4-digit PIN. You will need to enter it every time you open Readest. There is no way to recover a forgotten PIN. Clearing the app data is the only way to reset it.": "یک پین ۴ رقمی انتخاب کنید. هر بار که Readest را باز می‌کنید باید آن را وارد کنید. راهی برای بازیابی پین فراموش‌شده وجود ندارد. پاک کردن داده‌های برنامه تنها راه بازنشانی آن است.",
"Credentials": "اعتبارنامه‌ها",
"Tokens, usernames, and passwords for OPDS, KOReader, Hardcover, and Readwise. When disabled, credentials remain on this device only and are never uploaded.": "توکن‌ها، نام‌های کاربری و رمزهای عبور OPDS، KOReader، Hardcover و Readwise. هنگام غیرفعال بودن، اعتبارنامه‌ها فقط روی این دستگاه باقی می‌مانند و هرگز بارگذاری نمی‌شوند.",
"Sensitive synced fields are encrypted before upload. Set a passphrase now or later when encryption is needed.": "فیلدهای حساسِ همگام‌سازی‌شده پیش از بارگذاری رمزگذاری می‌شوند. اکنون یا بعداً، هنگامی که به رمزگذاری نیاز شد، یک عبارت عبور تنظیم کنید.",
"Saved to this account. You will be prompted for it when decrypting credentials.": "در این حساب ذخیره شد. هنگام رمزگشایی اعتبارنامه‌ها از شما خواسته می‌شود.",
"Reading progress on this device differs from \"{{deviceName}}\".": "پیشرفت مطالعه در این دستگاه با «{{deviceName}}» تفاوت دارد.",
"This device": "این دستگاه",
"Red": "قرمز",
"Yellow": "زرد",
"Green": "سبز",
"Blue": "آبی",
"Violet": "بنفش",
"Edit color": "ویرایش رنگ",
"Add custom color": "افزودن رنگ سفارشی",
"Add label": "افزودن برچسب",
"Choose color": "انتخاب رنگ",
"Connected to Hardcover. Last synced {{time}}.": "متصل به Hardcover. آخرین همگام‌سازی {{time}}.",
"Integrations": "یکپارچه‌سازی‌ها",
"Hardcover": "Hardcover",
"Connected to Readwise. Last synced {{time}}.": "متصل به Readwise. آخرین همگام‌سازی {{time}}.",
"Readwise": "Readwise",
"Browse and download books from online catalogs.": "کتاب‌ها را از کاتالوگ‌های آنلاین مرور و دانلود کنید.",
"Connected as {{user}}": "متصل به عنوان {{user}}",
"Not connected": "متصل نیست",
"{{count}} catalog_one": "{{count}} کاتالوگ",
"{{count}} catalog_other": "{{count}} کاتالوگ",
"No catalogs": "بدون کاتالوگ",
"Connect Readest to external services for sync, highlights, and catalogs.": "Readest را برای همگام‌سازی، هایلایت‌ها و کاتالوگ‌ها به سرویس‌های خارجی متصل کنید.",
"Reading Sync": "همگام‌سازی مطالعه",
"Content Sources": "منابع محتوا",
"Scroll tabs": "پیمایش زبانه‌ها",
"Catalog actions": "اقدامات فهرست",
"File Content": "محتوای فایل",
"Start your library": "کتابخانه‌ی خود را آغاز کنید",
"Pick a book from your device to add it to your library.": "از دستگاه خود کتابی انتخاب کنید تا به کتابخانه‌تان افزوده شود.",
"Drop a book anywhere on this window, or pick one from your computer.": "کتابی را در هر جای این پنجره رها کنید، یا یکی را از رایانه‌ی خود انتخاب کنید.",
"Sign in to sync your library": "برای همگام‌سازی کتابخانه‌ی خود وارد شوید"
}
@@ -102,11 +102,9 @@
"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.",
"Wikipedia": "Wikipédia",
"Writing Mode": "Mode écriture",
"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",
"Apply to All Books": "Appliquer à tous les livres",
"Apply to This Book": "Appliquer à ce livre",
@@ -172,9 +170,6 @@
"Token": "Code de vérification",
"Your OTP token": "Votre code OTP",
"Verify token": "Vérifier le code",
"Sign in with Google": "Se connecter avec Google",
"Sign in with Apple": "Se connecter avec Apple",
"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.",
"Community Support": "Support communautaire",
@@ -187,7 +182,6 @@
"RTL Direction": "Direction RTL",
"Maximum Column Height": "Hauteur maximale de colonne",
"Maximum Column Width": "Largeur maximale de colonne",
"Continuous Scroll": "Défilement continu",
"Fullscreen": "Plein écran",
"No supported files found. Supported formats: {{formats}}": "Aucun fichier pris en charge trouvé. Formats pris en charge : {{formats}}",
"Drop to Import Books": "Déposez pour importer des livres",
@@ -305,7 +299,6 @@
"No notes match your search": "Aucune note trouvée",
"Search notes and excerpts...": "Rechercher des notes...",
"Sign in to Sync": "Connectez-vous pour synchroniser",
"Synced at {{time}}": "Synchronisé à {{time}}",
"Never synced": "Jamais synchronisé",
"Show Remaining Time": "Afficher le temps restant",
"{{time}} min left in chapter": "{{time}} min restant dans le chapitre",
@@ -340,12 +333,12 @@
"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",
"{{count}} pages left in chapter_one": "<0>{{count}}</0><1> page restant dans le chapitre</1>",
"{{count}} pages left in chapter_many": "<0>{{count}}</0><1> pages restantes dans le chapitre</1>",
"{{count}} pages left in chapter_other": "<0>{{count}}</0><1> pages restantes dans le chapitre</1>",
"Show Remaining Pages": "Voir restantes",
"Source Han Serif CN VF": "Source Han Serif",
"Huiwen-mincho": "Huiwen Mincho",
"Source Han Serif CN": "Source Han Serif",
"Huiwen-MinchoGBK": "Huiwen Mincho",
"KingHwa_OldSong": "KingHwa Song",
"Manage Subscription": "Gérer labonnement",
"Coming Soon": "Bientôt disponible",
@@ -470,14 +463,12 @@
"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",
"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",
"Connect to your KOReader Sync server.": "Connectez-vous à votre serveur de synchronisation KOReader.",
@@ -488,10 +479,7 @@
"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}}%)",
@@ -606,11 +594,10 @@
"Size": "Taille",
"Cover": "Couverture",
"Contain": "Contenir",
"{{number}} pages left in chapter": "{{number}} pages restantes dans le chapitre",
"{{number}} pages left in chapter": "<0>{{number}}</0><1> pages restantes dans le chapitre</1>",
"Device": "Appareil",
"E-Ink Mode": "Mode E-Ink",
"Highlight Colors": "Couleurs de Surlignage",
"Auto Screen Brightness": "Luminosité Automatique de l'Écran",
"Pagination": "Pagination",
"Disable Double Tap": "Désactiver le Double Tap",
"Tap to Paginate": "Tapoter pour Paginater",
@@ -645,7 +632,6 @@
"Strikethrough": "Barré",
"Squiggly": "Ondulé",
"Outline": "Contour",
"Save Current Color": "Enregistrer la Couleur Actuelle",
"Quick Colors": "Couleurs Rapides",
"Highlighter": "Surligneur",
"Save Book Cover": "Enregistrer la Couverture du Livre",
@@ -703,9 +689,7 @@
"Validating...": "Validation...",
"View All": "Tout afficher",
"Forward": "Suivant",
"OPDS Catalog": "Catalogue OPDS",
"Home": "Accueil",
"Library": "Bibliothèque",
"{{count}} items_one": "{{count}} élément",
"{{count}} items_many": "{{count}} éléments",
"{{count}} items_other": "{{count}} éléments",
@@ -726,5 +710,732 @@
"Last": "Dernier",
"Cannot Load Page": "Impossible de charger la page",
"An error occurred": "Une erreur est survenue",
"Online Library": "Bibliothèque en ligne"
"Online Library": "Bibliothèque en ligne",
"URL must start with http:// or https://": "URL doit commencer par http:// ou https://",
"Title, Author, Tag, etc...": "Titre, Auteur, Tag, etc...",
"Query": "Requête",
"Subject": "Sujet",
"Enter {{terms}}": "Entrez {{terms}}",
"No search results found": "Aucun résultat trouvé",
"Failed to load OPDS feed: {{status}} {{statusText}}": "Échec du chargement du flux OPDS : {{status}} {{statusText}}",
"Search in {{title}}": "Rechercher dans {{title}}",
"Manage Storage": "Gérer le stockage",
"Failed to load files": "Échec du chargement des fichiers",
"Deleted {{count}} file(s)_one": "{{count}} fichier supprimé",
"Deleted {{count}} file(s)_many": "{{count}} fichiers supprimés",
"Deleted {{count}} file(s)_other": "{{count}} fichiers supprimés",
"Failed to delete {{count}} file(s)_one": "Échec de la suppression de {{count}} fichier",
"Failed to delete {{count}} file(s)_many": "Échec de la suppression de {{count}} fichiers",
"Failed to delete {{count}} file(s)_other": "Échec de la suppression de {{count}} fichiers",
"Failed to delete files": "Échec de la suppression des fichiers",
"Total Files": "Nombre total de fichiers",
"Total Size": "Taille totale",
"Quota": "Quota",
"Used": "Utilisé",
"Files": "Fichiers",
"Search files...": "Rechercher des fichiers...",
"Newest First": "Les plus récents",
"Oldest First": "Les plus anciens",
"Largest First": "Les plus volumineux",
"Smallest First": "Les moins volumineux",
"Name A-Z": "Nom A-Z",
"Name Z-A": "Nom Z-A",
"{{count}} selected_one": "{{count}} sélectionné",
"{{count}} selected_many": "{{count}} sélectionnés",
"{{count}} selected_other": "{{count}} sélectionnés",
"Delete Selected": "Supprimer la sélection",
"Created": "Créé",
"No files found": "Aucun fichier trouvé",
"No files uploaded yet": "Aucun fichier téléchargé pour le moment",
"files": "fichiers",
"Page {{current}} of {{total}}": "Page {{current}} sur {{total}}",
"Are you sure to delete {{count}} selected file(s)?_one": "Voulez-vous vraiment supprimer {{count}} fichier sélectionné ?",
"Are you sure to delete {{count}} selected file(s)?_many": "Voulez-vous vraiment supprimer {{count}} fichiers sélectionnés ?",
"Are you sure to delete {{count}} selected file(s)?_other": "Voulez-vous vraiment supprimer {{count}} fichiers sélectionnés ?",
"Cloud Storage Usage": "Utilisation du stockage cloud",
"Rename Group": "Renommer le groupe",
"From Directory": "Depuis le répertoire",
"Successfully imported {{count}} book(s)_one": "Importation réussie de 1 livre",
"Successfully imported {{count}} book(s)_many": "Importation réussie de {{count}} livres",
"Successfully imported {{count}} book(s)_other": "Importation réussie de {{count}} livres",
"Count": "Nombre",
"Start Page": "Page de départ",
"Search in OPDS Catalog...": "Rechercher dans le catalogue OPDS...",
"Please log in to use advanced TTS features": "Veuillez vous connecter pour utiliser les fonctionnalités avancées de TTS",
"Word limit of 30 words exceeded.": "La limite de 30 mots a été dépassée.",
"Proofread": "Correction",
"Current selection": "Sélection actuelle",
"All occurrences in this book": "Toutes les occurrences dans ce livre",
"All occurrences in your library": "Toutes les occurrences dans votre bibliothèque",
"Selected text:": "Texte sélectionné :",
"Replace with:": "Remplacer par :",
"Enter text...": "Saisir du texte…",
"Case sensitive:": "Respecter la casse :",
"Scope:": "Portée :",
"Selection": "Sélection",
"Library": "Bibliothèque",
"Yes": "Oui",
"No": "Non",
"Proofread Replacement Rules": "Règles de remplacement de correction",
"Selected Text Rules": "Règles du texte sélectionné",
"No selected text replacement rules": "Aucune règle de remplacement pour le texte sélectionné",
"Book Specific Rules": "Règles spécifiques au livre",
"No book-level replacement rules": "Aucune règle de remplacement au niveau du livre",
"Disable Quick Action": "Désactiver laction rapide",
"Enable Quick Action on Selection": "Activer laction rapide lors de la sélection",
"None": "Aucune",
"Annotation Tools": "Outils dannotation",
"Enable Quick Actions": "Activer les actions rapides",
"Quick Action": "Action rapide",
"Copy to Notebook": "Copier dans le carnet",
"Copy text after selection": "Copier le texte après la sélection",
"Highlight text after selection": "Mettre en surbrillance le texte après la sélection",
"Annotate text after selection": "Annoter le texte après la sélection",
"Search text after selection": "Rechercher le texte après la sélection",
"Look up text in dictionary after selection": "Chercher le texte dans le dictionnaire après la sélection",
"Translate text after selection": "Traduire le texte après la sélection",
"Read text aloud after selection": "Lire le texte à haute voix après la sélection",
"Proofread text after selection": "Relire le texte après la sélection",
"{{activeCount}} active, {{pendingCount}} pending": "{{activeCount}} actifs, {{pendingCount}} en attente",
"{{failedCount}} failed": "{{failedCount}} échoués",
"Waiting...": "En attente...",
"Failed": "Échoué",
"Completed": "Terminé",
"Cancelled": "Annulé",
"Retry": "Réessayer",
"Active": "Actifs",
"Transfer Queue": "File de transfert",
"Upload All": "Tout téléverser",
"Download All": "Tout télécharger",
"Resume Transfers": "Reprendre les transferts",
"Pause Transfers": "Suspendre les transferts",
"Pending": "En attente",
"No transfers": "Aucun transfert",
"Retry All": "Tout réessayer",
"Clear Completed": "Effacer les terminés",
"Clear Failed": "Effacer les échoués",
"Upload queued: {{title}}": "Téléversement en file: {{title}}",
"Download queued: {{title}}": "Téléchargement en file: {{title}}",
"Book not found in library": "Livre non trouvé dans la bibliothèque",
"Unknown error": "Erreur inconnue",
"Please log in to continue": "Veuillez vous connecter pour continuer",
"Cloud File Transfers": "Transferts de fichiers cloud",
"Show Search Results": "Afficher les résultats",
"Search results for '{{term}}'": "Résultats pour « {{term}} »",
"Close Search": "Fermer la recherche",
"Previous Result": "Résultat précédent",
"Next Result": "Résultat suivant",
"Bookmarks": "Signets",
"Annotations": "Annotations",
"Show Results": "Afficher les résultats",
"Clear search": "Effacer la recherche",
"Clear search history": "Effacer l'historique de recherche",
"Tap to Toggle Footer": "Appuyez pour afficher/masquer le pied de page",
"Show Current Time": "Afficher l'heure actuelle",
"Use 24 Hour Clock": "Utiliser le format 24 heures",
"Show Current Battery Status": "Afficher l'état actuel de la batterie",
"Exported successfully": "Exporté avec succès",
"Book exported successfully.": "Livre exporté avec succès.",
"Failed to export the book.": "Échec de l'exportation du livre.",
"Export Book": "Exporter le livre",
"Whole word:": "Mot entier :",
"Date Published": "Date de publication",
"Only for TTS:": "Uniquement pour TTS :",
"Uploaded": "Téléversé",
"Downloaded": "Téléchargé",
"Deleted": "Supprimé",
"Note:": "Note :",
"Time:": "Heure :",
"Format Options": "Options de format",
"Export Date": "Date d'export",
"Chapter Titles": "Titres de chapitres",
"Chapter Separator": "Séparateur de chapitres",
"Highlights": "Surlignages",
"Note Date": "Date de note",
"Advanced": "Avancé",
"Hide": "Masquer",
"Show": "Afficher",
"Use Custom Template": "Utiliser un modèle personnalisé",
"Export Template": "Modèle d'export",
"Template Syntax:": "Syntaxe du modèle :",
"Insert value": "Insérer une valeur",
"Format date (locale)": "Formater la date (locale)",
"Format date (custom)": "Formater la date (personnalisé)",
"Conditional": "Conditionnel",
"Loop": "Boucle",
"Available Variables:": "Variables disponibles :",
"Book title": "Titre du livre",
"Book author": "Auteur du livre",
"Export date": "Date d'export",
"Array of chapters": "Liste de chapitres",
"Chapter title": "Titre du chapitre",
"Array of annotations": "Liste d'annotations",
"Highlighted text": "Texte surligné",
"Annotation note": "Note d'annotation",
"Date Format Tokens:": "Jetons de format de date :",
"Year (4 digits)": "Année (4 chiffres)",
"Month (01-12)": "Mois (01-12)",
"Day (01-31)": "Jour (01-31)",
"Hour (00-23)": "Heure (00-23)",
"Minute (00-59)": "Minute (00-59)",
"Second (00-59)": "Seconde (00-59)",
"Show Source": "Afficher la source",
"No content to preview": "Aucun contenu à prévisualiser",
"Export": "Exporter",
"Set Timeout": "Définir le délai",
"Select Voice": "Sélectionner la voix",
"Toggle Sticky Bottom TTS Bar": "Basculer la barre TTS fixée",
"Display what I'm reading on Discord": "Afficher ce que je lis sur Discord",
"Show on Discord": "Afficher sur Discord",
"Instant {{action}}": "{{action}} instantané",
"Instant {{action}} Disabled": "{{action}} instantané désactivé",
"Annotation": "Annotation",
"Reset Template": "Réinitialiser le modèle",
"Annotation style": "Style d'annotation",
"Annotation color": "Couleur d'annotation",
"Annotation time": "Heure d'annotation",
"AI": "IA",
"Are you sure you want to re-index this book?": "Voulez-vous vraiment réindexer ce livre ?",
"Enable AI in Settings": "Activer l'IA dans les paramètres",
"Index This Book": "Indexer ce livre",
"Enable AI search and chat for this book": "Activer la recherche IA et le chat pour ce livre",
"Start Indexing": "Démarrer l'indexation",
"Indexing book...": "Indexation du livre...",
"Preparing...": "Préparation...",
"Delete this conversation?": "Supprimer cette conversation ?",
"No conversations yet": "Pas encore de conversations",
"Start a new chat to ask questions about this book": "Démarrez une nouvelle conversation pour poser des questions sur ce livre",
"Rename": "Renommer",
"New Chat": "Nouvelle conversation",
"Chat": "Chat",
"Please enter a model ID": "Veuillez entrer un ID de modèle",
"Model not available or invalid": "Modèle non disponible ou invalide",
"Failed to validate model": "Échec de la validation du modèle",
"Couldn't connect to Ollama. Is it running?": "Impossible de se connecter à Ollama. Est-il en cours d'exécution ?",
"Invalid API key or connection failed": "Clé API invalide ou échec de connexion",
"Connection failed": "Échec de connexion",
"AI Assistant": "Assistant IA",
"Enable AI Assistant": "Activer l'assistant IA",
"Provider": "Fournisseur",
"Ollama (Local)": "Ollama (Local)",
"AI Gateway (Cloud)": "Passerelle IA (Cloud)",
"Ollama Configuration": "Configuration Ollama",
"Refresh Models": "Actualiser les modèles",
"AI Model": "Modèle IA",
"No models detected": "Aucun modèle détecté",
"AI Gateway Configuration": "Configuration de la passerelle IA",
"Choose from a selection of high-quality, economical AI models. You can also bring your own model by selecting \"Custom Model\" below.": "Choisissez parmi une sélection de modèles IA de haute qualité et économiques. Vous pouvez également utiliser votre propre modèle en sélectionnant \"Modèle personnalisé\" ci-dessous.",
"API Key": "Clé API",
"Get Key": "Obtenir la clé",
"Model": "Modèle",
"Custom Model...": "Modèle personnalisé...",
"Custom Model ID": "ID du modèle personnalisé",
"Validate": "Valider",
"Model available": "Modèle disponible",
"Connection": "Connexion",
"Test Connection": "Tester la connexion",
"Connected": "Connecté",
"Custom Colors": "Couleurs personnalisées",
"Color E-Ink Mode": "Mode E-Ink couleur",
"Reading Ruler": "Règle de lecture",
"Enable Reading Ruler": "Activer la règle de lecture",
"Lines to Highlight": "Lignes à mettre en évidence",
"Ruler Color": "Couleur de la règle",
"Command Palette": "Palette de commandes",
"Search settings and actions...": "Rechercher des paramètres et des actions...",
"No results found for": "Aucun résultat trouvé pour",
"Type to search settings and actions": "Saisissez pour rechercher des paramètres et des actions",
"Recent": "Récent",
"navigate": "naviguer",
"select": "sélectionner",
"close": "fermer",
"Search Settings": "Rechercher dans les paramètres",
"Page Margins": "Marges de page",
"AI Provider": "Fournisseur d'IA",
"Ollama URL": "URL Ollama",
"Ollama Model": "Modèle Ollama",
"AI Gateway Model": "Modèle AI Gateway",
"Actions": "Actions",
"Navigation": "Navigation",
"Set status for {{count}} book(s)_one": "Définir le statut pour {{count}} livre",
"Set status for {{count}} book(s)_other": "Définir le statut pour {{count}} livres",
"Mark as Unread": "Marquer comme non lu",
"Mark as Finished": "Marquer comme terminé",
"Finished": "Terminé",
"Unread": "Non lu",
"Clear Status": "Effacer le statut",
"Status": "Statut",
"Set status for {{count}} book(s)_many": "Définir le statut pour {{count}} livres",
"Loading": "Chargement...",
"Exit Paragraph Mode": "Quitter le mode paragraphe",
"Paragraph Mode": "Mode paragraphe",
"Embedding Model": "Modèle d'incorporation",
"{{count}} book(s) synced_one": "{{count}} livre synchronisé",
"{{count}} book(s) synced_many": "{{count}} livres synchronisés",
"{{count}} book(s) synced_other": "{{count}} livres synchronisés",
"Unable to start RSVP": "Impossible de démarrer RSVP",
"RSVP not supported for PDF": "RSVP non pris en charge pour PDF",
"Select Chapter": "Sélectionner le chapitre",
"Context": "Contexte",
"Ready": "Prêt",
"Chapter Progress": "Progression du chapitre",
"words": "mots",
"{{time}} left": "{{time}} restant",
"Reading progress": "Progression de la lecture",
"Skip back 15 words": "Reculer de 15 mots",
"Back 15 words (Shift+Left)": "Reculer de 15 mots (Shift+Left)",
"Pause (Space)": "Pause (Espace)",
"Play (Space)": "Lecture (Espace)",
"Skip forward 15 words": "Avancer de 15 mots",
"Forward 15 words (Shift+Right)": "Avancer de 15 mots (Shift+Right)",
"Decrease speed": "Diminuer la vitesse",
"Slower (Left/Down)": "Plus lent (Gauche/Bas)",
"Increase speed": "Augmenter la vitesse",
"Faster (Right/Up)": "Plus rapide (Droite/Haut)",
"Start RSVP Reading": "Démarrer la lecture RSVP",
"Choose where to start reading": "Choisir où commencer la lecture",
"From Chapter Start": "Depuis le début du chapitre",
"Start reading from the beginning of the chapter": "Commencer la lecture au début du chapitre",
"Resume": "Reprendre",
"Continue from where you left off": "Continuer là où vous vous êtes arrêté",
"From Current Page": "Depuis la page actuelle",
"Start from where you are currently reading": "Commencer là où vous lisez actuellement",
"From Selection": "Depuis la sélection",
"Speed Reading Mode": "Mode lecture rapide",
"Scroll left": "Défiler vers la gauche",
"Scroll right": "Défiler vers la droite",
"Library Sync Progress": "Progression de la synchronisation de la bibliothèque",
"Back to library": "Retour à la bibliothèque",
"Group by...": "Grouper par...",
"Export as Plain Text": "Exporter en texte brut",
"Export as Markdown": "Exporter en Markdown",
"Show Page Navigation Buttons": "Boutons de navigation",
"Page {{number}}": "Page {{number}}",
"highlight": "surlignage",
"underline": "soulignage",
"squiggly": "ondulé",
"red": "rouge",
"violet": "violet",
"blue": "bleu",
"green": "vert",
"yellow": "jaune",
"Select {{style}} style": "Sélectionner le style {{style}}",
"Select {{color}} color": "Sélectionner la couleur {{color}}",
"Close Book": "Fermer le livre",
"Speed Reading": "Lecture rapide",
"Close Speed Reading": "Fermer la lecture rapide",
"Authors": "Auteurs",
"Books": "Livres",
"Groups": "Groupes",
"Back to TTS Location": "Retour à l'emplacement TTS",
"Metadata": "Métadonnées",
"Image viewer": "Visionneuse d'images",
"Previous Image": "Image précédente",
"Next Image": "Image suivante",
"Zoomed": "Zoomé",
"Zoom level": "Niveau de zoom",
"Table viewer": "Visionneuse de tableaux",
"Unable to connect to Readwise. Please check your network connection.": "Impossible de se connecter à Readwise. Veuillez vérifier votre connexion réseau.",
"Invalid Readwise access token": "Jeton d'accès Readwise invalide",
"Disconnected from Readwise": "Déconnecté de Readwise",
"Never": "Jamais",
"Sync Enabled": "Synchronisation activée",
"Disconnect": "Déconnecter",
"Connect your Readwise account to sync highlights.": "Connectez votre compte Readwise pour synchroniser les surlignages.",
"Get your access token at": "Obtenez votre jeton d'accès sur",
"Access Token": "Jeton d'accès",
"Paste your Readwise access token": "Collez votre jeton d'accès Readwise",
"Readwise Sync": "Synchronisation Readwise",
"Push Highlights": "Pousser les surlignages",
"Highlights synced to Readwise": "Surlignages synchronisés avec Readwise",
"Readwise sync failed: no internet connection": "Échec de la synchronisation Readwise : pas de connexion internet",
"Readwise sync failed: {{error}}": "Échec de la synchronisation Readwise : {{error}}",
"System Screen Brightness": "Luminosité de l'écran système",
"Page:": "Page :",
"Page: {{number}}": "Page : {{number}}",
"Annotation page number": "Numéro de page de l'annotation",
"Translating...": "Traduction en cours...",
"Show Battery Percentage": "Afficher le pourcentage de batterie",
"Hide Scrollbar": "Masquer la barre de défilement",
"Skip to last reading position": "Aller à la dernière position de lecture",
"Show context": "Afficher le contexte",
"Hide context": "Masquer le contexte",
"Decrease font size": "Réduire la taille de police",
"Increase font size": "Augmenter la taille de police",
"Focus": "Focus",
"Theme color": "Couleur du thème",
"Import failed": "Échec de l'importation",
"Available Formatters:": "Formateurs disponibles :",
"Format date": "Formater la date",
"Markdown block quote (> per line)": "Citation Markdown (> par ligne)",
"Newlines to <br>": "Retours à la ligne en <br>",
"Change case": "Changer la casse",
"Trim whitespace": "Supprimer les espaces",
"Truncate to n characters": "Tronquer à n caractères",
"Replace text": "Remplacer le texte",
"Fallback value": "Valeur par défaut",
"Get length": "Obtenir la longueur",
"First/last element": "Premier/dernier élément",
"Join array": "Joindre le tableau",
"Backup failed: {{error}}": "Échec de la sauvegarde : {{error}}",
"Select Backup": "Sélectionner une sauvegarde",
"Restore failed: {{error}}": "Échec de la restauration : {{error}}",
"Backup & Restore": "Sauvegarde et restauration",
"Create a backup of your library or restore from a previous backup. Restoring will merge with your current library.": "Créez une sauvegarde de votre bibliothèque ou restaurez à partir d'une sauvegarde précédente. La restauration fusionnera avec votre bibliothèque actuelle.",
"Backup Library": "Sauvegarder la bibliothèque",
"Restore Library": "Restaurer la bibliothèque",
"Creating backup...": "Création de la sauvegarde...",
"Restoring library...": "Restauration de la bibliothèque...",
"{{current}} of {{total}} items": "{{current}} sur {{total}} éléments",
"Backup completed successfully!": "Sauvegarde terminée avec succès !",
"Restore completed successfully!": "Restauration terminée avec succès !",
"Your library has been saved to the selected location.": "Votre bibliothèque a été enregistrée à l'emplacement sélectionné.",
"{{added}} books added, {{updated}} books updated.": "{{added}} livres ajoutés, {{updated}} livres mis à jour.",
"Operation failed": "L'opération a échoué",
"Loading library...": "Chargement de la bibliothèque...",
"{{count}} books refreshed_one": "{{count}} livre rafraîchi",
"{{count}} books refreshed_many": "{{count}} livres rafraîchis",
"{{count}} books refreshed_other": "{{count}} livres rafraîchis",
"Failed to refresh metadata": "Échec du rafraîchissement des métadonnées",
"Refresh Metadata": "Rafraîchir les métadonnées",
"Keyboard Shortcuts": "Raccourcis clavier",
"View all keyboard shortcuts": "Voir tous les raccourcis clavier",
"Switch Sidebar Tab": "Changer l'onglet de la barre latérale",
"Toggle Notebook": "Afficher/masquer le carnet",
"Search in Book": "Rechercher dans le livre",
"Toggle Scroll Mode": "Basculer le mode défilement",
"Toggle Select Mode": "Basculer le mode sélection",
"Toggle Bookmark": "Basculer le signet",
"Toggle Text to Speech": "Basculer la synthèse vocale",
"Play / Pause TTS": "Lecture / Pause TTS",
"Toggle Paragraph Mode": "Basculer le mode paragraphe",
"Highlight Selection": "Surligner la sélection",
"Underline Selection": "Souligner la sélection",
"Annotate Selection": "Annoter la sélection",
"Search Selection": "Rechercher la sélection",
"Copy Selection": "Copier la sélection",
"Translate Selection": "Traduire la sélection",
"Dictionary Lookup": "Recherche dans le dictionnaire",
"Read Aloud Selection": "Lire la sélection à voix haute",
"Proofread Selection": "Relire la sélection",
"Open Settings": "Ouvrir les paramètres",
"Open Command Palette": "Ouvrir la palette de commandes",
"Show Keyboard Shortcuts": "Afficher les raccourcis clavier",
"Open Books": "Ouvrir les livres",
"Toggle Fullscreen": "Basculer en plein écran",
"Close Window": "Fermer la fenêtre",
"Quit App": "Quitter l'application",
"Go Left / Previous Page": "Aller à gauche / Page précédente",
"Go Right / Next Page": "Aller à droite / Page suivante",
"Go Up": "Aller en haut",
"Go Down": "Aller en bas",
"Previous Chapter": "Chapitre précédent",
"Next Chapter": "Chapitre suivant",
"Scroll Half Page Down": "Défiler d'une demi-page vers le bas",
"Scroll Half Page Up": "Défiler d'une demi-page vers le haut",
"Save Note": "Enregistrer la note",
"Single Section Scroll": "Défilement par section",
"General": "Général",
"Text to Speech": "Synthèse vocale",
"Zoom": "Zoom",
"Window": "Fenêtre",
"Your Bookshelf": "Votre bibliothèque",
"TTS": "Synthèse vocale",
"Media Info": "Infos média",
"Update Frequency": "Fréquence de mise à jour",
"Every Sentence": "Chaque phrase",
"Every Paragraph": "Chaque paragraphe",
"Every Chapter": "Chaque chapitre",
"TTS Media Info Update Frequency": "Fréquence de mise à jour des infos média TTS",
"Select reading speed": "Sélectionner la vitesse de lecture",
"Drag to seek": "Glisser pour chercher",
"Punctuation Delay": "Délai de ponctuation",
"Please confirm that this OPDS connection will be proxied through Readest servers on the web app before continuing.": "Veuillez confirmer que cette connexion OPDS sera acheminée via les serveurs Readest sur l'application web avant de continuer.",
"Custom Headers (optional)": "En-têtes personnalisés (facultatif)",
"Add one header per line using \"Header-Name: value\".": "Ajoutez un en-tête par ligne en utilisant \"Header-Name: value\".",
"I understand this OPDS connection will be proxied through Readest servers on the web app. If I do not trust Readest with these credentials or headers, I should use the native app instead.": "Je comprends que cette connexion OPDS sera acheminée via les serveurs Readest sur l'application web. Si je ne fais pas confiance à Readest pour ces identifiants ou en-têtes, je devrais utiliser l'application native.",
"Unable to connect to Hardcover. Please check your network connection.": "Impossible de se connecter à Hardcover. Veuillez vérifier votre connexion réseau.",
"Invalid Hardcover API token": "Jeton API Hardcover invalide",
"Disconnected from Hardcover": "Déconnecté de Hardcover",
"Connect your Hardcover account to sync reading progress and notes.": "Connectez votre compte Hardcover pour synchroniser la progression de lecture et les notes.",
"Get your API token from hardcover.app → Settings → API.": "Obtenez votre jeton API depuis hardcover.app → Paramètres → API.",
"API Token": "Jeton API",
"Paste your Hardcover API token": "Collez votre jeton API Hardcover",
"Hardcover Sync": "Synchronisation Hardcover",
"Push Notes": "Envoyer les notes",
"No annotations or excerpts to sync for this book.": "Aucune annotation ni extrait à synchroniser pour ce livre.",
"Configure Hardcover in Settings first.": "Configurez d'abord Hardcover dans les paramètres.",
"No new Hardcover note changes to sync.": "Aucun nouveau changement de notes Hardcover à synchroniser.",
"Hardcover synced: {{inserted}} new, {{updated}} updated, {{skipped}} unchanged": "Hardcover synchronisé : {{inserted}} nouveaux, {{updated}} mis à jour, {{skipped}} inchangés",
"Hardcover notes sync failed: {{error}}": "Échec de la synchronisation des notes Hardcover : {{error}}",
"Reading progress synced to Hardcover": "Progression de lecture synchronisée avec Hardcover",
"Hardcover progress sync failed: {{error}}": "Échec de la synchronisation de la progression Hardcover : {{error}}",
"ISBN": "ISBN",
"Keep existing source identifier": "Conserver l'identifiant source existant",
"Toggle Toolbar": "Basculer la barre d'outils",
"Split Hyphens": "Séparer les traits d'union",
"Apply Theme Colors to PDF": "Appliquer les couleurs du thème au PDF",
"Name": "Nom",
"Remove": "Supprimer",
"Edit OPDS Catalog": "Modifier le catalogue OPDS",
"Save Changes": "Enregistrer les modifications",
"Use Book Layout": "Utiliser la mise en page du livre",
"End of this section. Continue to the next.": "Fin de cette section. Continuer à la suivante.",
"Auto-download": "Téléchargement automatique",
"Auto-download new items": "Télécharger automatiquement les nouveaux éléments",
"Automatically download new publications when the app syncs": "Télécharger automatiquement les nouvelles publications lors de la synchronisation de l'application",
"{{count}} new item(s) downloaded from OPDS_one": "{{count}} nouvel élément téléchargé depuis OPDS",
"{{count}} new item(s) downloaded from OPDS_many": "{{count}} nouveaux éléments téléchargés depuis OPDS",
"{{count}} new item(s) downloaded from OPDS_other": "{{count}} nouveaux éléments téléchargés depuis OPDS",
"Failed to sync {{count}} OPDS catalog(s)_one": "Échec de la synchronisation de {{count}} catalogue OPDS",
"Failed to sync {{count}} OPDS catalog(s)_many": "Échec de la synchronisation de {{count}} catalogues OPDS",
"Failed to sync {{count}} OPDS catalog(s)_other": "Échec de la synchronisation de {{count}} catalogues OPDS",
"Last synced {{when}}": "Dernière synchronisation {{when}}",
"Failed downloads": "Téléchargements échoués",
"No failed downloads": "Aucun téléchargement échoué",
"Attempts: {{count}}_one": "Tentatives : {{count}}",
"Attempts: {{count}}_many": "Tentatives : {{count}}",
"Attempts: {{count}}_other": "Tentatives : {{count}}",
"Skip": "Ignorer",
"Skip all": "Tout ignorer",
"Retry all": "Tout réessayer",
"{{count}} failed_one": "{{count}} échec",
"{{count}} failed_many": "{{count}} échecs",
"{{count}} failed_other": "{{count}} échecs",
"(none)": "(aucun)",
"Identifiers": "Identifiants",
"Read (Stream)": "Lire (Flux)",
"Failed to start stream": "Échec du démarrage du flux",
"No dictionaries enabled": "Aucun dictionnaire activé",
"Enable a dictionary in Settings → Language → Dictionaries.": "Activez un dictionnaire dans Paramètres → Langue → Dictionnaires.",
"Wiktionary": "Wiktionnaire",
"Drag to reorder": "Glisser pour réorganiser",
"Built-in": "Intégré",
"Bundle is missing on this device. Re-import to use it.": "Le paquet est absent sur cet appareil. Ré-importez-le pour lutiliser.",
"This dictionary format is not supported.": "Ce format de dictionnaire nest pas pris en charge.",
"MDict": "MDict",
"StarDict": "StarDict",
"Imported {{count}} dictionary_one": "{{count}} dictionnaire importé",
"Imported {{count}} dictionary_many": "{{count}} dictionnaires importés",
"Imported {{count}} dictionary_other": "{{count}} dictionnaires importés",
"Skipped incomplete bundles: {{names}}": "Paquets incomplets ignorés : {{names}}",
"Failed to import dictionary: {{message}}": "Échec de limport du dictionnaire : {{message}}",
"Dictionaries": "Dictionnaires",
"Delete Dictionary": "Supprimer le dictionnaire",
"Importing…": "Importation…",
"Import Dictionary": "Importer un dictionnaire",
"No dictionaries available.": "Aucun dictionnaire disponible.",
"StarDict bundles need .ifo, .idx, and .dict.dz files (.syn optional).": "Les paquets StarDict nécessitent des fichiers .ifo, .idx et .dict.dz (.syn optionnel).",
"Select all the bundle files together when importing.": "Sélectionnez tous les fichiers du paquet ensemble lors de limport.",
"Manage Dictionaries": "Gérer les dictionnaires",
"Select Dictionary Files": "Sélectionner les fichiers du dictionnaire",
"Read on Wikipedia →": "Lire sur Wikipédia →",
"This link can't be opened": "Impossible d'ouvrir ce lien",
"The annotation link is missing required information. The original link may have been truncated.": "Le lien d'annotation manque d'informations requises. Le lien d'origine a peut-être été tronqué.",
"Go to Readest": "Aller sur Readest",
"Open-source ebook reader for everyone, on every device.": "Lecteur d'e-books open source pour tout le monde, sur tous les appareils.",
"Open in Readest": "Ouvrir dans Readest",
"If Readest didn't open automatically, choose an option below:": "Si Readest ne s'est pas ouvert automatiquement, choisissez une option ci-dessous :",
"Continue reading where you left off.": "Reprenez votre lecture là où vous l'aviez laissée.",
"Readest logo": "Logo Readest",
"Opening Readest...": "Ouverture de Readest…",
"Open in Readest app": "Ouvrir dans l'application Readest",
"Continue in browser": "Continuer dans le navigateur",
"Don't have Readest?": "Vous n'avez pas Readest ?",
"Book not in your library": "Livre absent de votre bibliothèque",
"Share Book": "Partager le livre",
"Could not create share link": "Impossible de créer le lien de partage",
"Link copied": "Lien copié",
"Could not copy link": "Impossible de copier le lien",
"Share revoked": "Partage révoqué",
"Could not revoke share": "Impossible de révoquer le partage",
"Uploading book…": "Téléchargement du livre…",
"Generating…": "Génération…",
"Generate share link": "Créer un lien de partage",
"Share URL": "URL de partage",
"Copy link": "Copier le lien",
"Copied": "Copié",
"Share via…": "Partager via…",
"Expires {{date}}": "Expire le {{date}}",
"Revoking…": "Révocation…",
"Revoke share": "Révoquer le partage",
"Sign in to share books": "Connectez-vous pour partager des livres",
"Expiring soon": "Expire bientôt",
"Missing share token": "Jeton de partage manquant",
"Could not add to your library": "Impossible d'ajouter à votre bibliothèque",
"This share link is no longer available": "Ce lien de partage n'est plus disponible",
"The original link may have expired or been revoked.": "Le lien original a peut-être expiré ou été révoqué.",
"Get Readest": "Obtenir Readest",
"Loading shared book…": "Chargement du livre partagé…",
"Adding…": "Ajout…",
"Add to my library": "Ajouter à ma bibliothèque",
"Could not load your shares": "Impossible de charger vos partages",
"Revoked": "Révoqué",
"Expired": "Expiré",
"Shared books": "Livres partagés",
"You haven't shared any books yet": "Vous navez encore partagé aucun livre",
"Open a book and tap Share to send it to a friend.": "Ouvrez un livre et appuyez sur Partager pour lenvoyer à un ami.",
"{{count}} active_one": "{{count}} actif",
"{{count}} active_many": "{{count}} actifs",
"{{count}} active_other": "{{count}} actifs",
"{{count}} downloads_one": "{{count}} téléchargement",
"{{count}} downloads_many": "{{count}} téléchargements",
"{{count}} downloads_other": "{{count}} téléchargements",
"starts at saved page": "commence à la page enregistrée",
"More actions": "Plus dactions",
"Loading…": "Chargement…",
"Load more": "Charger plus",
"Could not load shared book": "Impossible de charger le livre partagé",
"The share link is missing required information.": "Il manque des informations nécessaires au lien de partage.",
"Please check your connection and try again.": "Vérifiez votre connexion et réessayez.",
"Sign in to import shared books": "Connectez-vous pour importer des livres partagés",
"Already in your library": "Déjà dans votre bibliothèque",
"Added to your library": "Ajouté à votre bibliothèque",
"Could not import shared book": "Impossible d'importer le livre partagé",
"Manage Shared Links": "Gérer les liens de partage",
"Expires in": "Expire dans",
"{{count}} days_one": "1 jour",
"{{count}} days_many": "{{count}} jours",
"{{count}} days_other": "{{count}} jours",
"Expires in {{count}} days_one": "Expire dans 1 jour",
"Expires in {{count}} days_many": "Expire dans {{count}} jours",
"Expires in {{count}} days_other": "Expire dans {{count}} jours",
"Expires in {{count}} hours_one": "Expire dans 1 heure",
"Expires in {{count}} hours_many": "Expire dans {{count}} heures",
"Expires in {{count}} hours_other": "Expire dans {{count}} heures",
"Open in app": "Ouvrir dans l'app",
"Shared with you": "Partagé avec vous",
"Downloading… {{percent}}%": "Téléchargement… {{percent}} %",
"Import progress": "Progression de limportation",
"Share reading progress": "Partager la progression de lecture",
"Includes your reading progress": "Inclut votre progression de lecture",
"Synced {{time}}": "Synchronisé {{time}}",
"Sync Info": "Infos de synchronisation",
"Last Synced": "Dernière synchronisation",
"DICT": "DICT",
"Slob": "Slob",
"DICT bundles need a .index file and a .dict.dz file.": "Les paquets DICT nécessitent un fichier .index et un fichier .dict.dz.",
"Slob bundles need a .slob file.": "Les paquets Slob nécessitent un fichier .slob.",
"URL template must start with http(s):// and contain %WORD%.": "Le modèle d'URL doit commencer par http(s):// et contenir %WORD%.",
"Web": "Web",
"Add Web Search": "Ajouter une recherche web",
"Edit Web Search": "Modifier la recherche web",
"e.g. Google": "ex. Google",
"URL Template": "Modèle d'URL",
"Use %WORD% where the looked-up word should appear.": "Utilisez %WORD% à l'endroit où le mot recherché doit apparaître.",
"Open the search result in your browser:": "Ouvrir le résultat de la recherche dans votre navigateur :",
"Open in {{name}}": "Ouvrir dans {{name}}",
"Book Fingerprint": "Empreinte du livre",
"Search the web": "Rechercher sur le web",
"App deeplink (readest://)": "Lien profond de lapplication (readest://)",
"Universal web link (https://)": "Lien web universel (https://)",
"Name cannot be empty.": "Le nom ne peut pas être vide.",
"Replaced {{count}} existing dictionary_one": "{{count}} dictionnaire existant remplacé",
"Replaced {{count}} existing dictionary_many": "{{count}} dictionnaires existants remplacés",
"Replaced {{count}} existing dictionary_other": "{{count}} dictionnaires existants remplacés",
"Cancel Edit": "Annuler la modification",
"Edit Dictionary": "Modifier le dictionnaire",
"MDict bundles use .mdx files; companion .mdd and .css files are optional.": "Les bundles MDict utilisent des fichiers .mdx ; les fichiers .mdd et .css associés sont facultatifs.",
"Dictionary name": "Nom du dictionnaire",
"This audio can't play here — the dictionary uses an outdated format. Try one with Opus, MP3, or WAV audio.": "Cet audio ne peut pas être lu ici — le dictionnaire utilise un format obsolète. Essayez-en un avec un audio Opus, MP3 ou WAV.",
"File uploaded: {{title}}": "Fichier téléchargé : {{title}}",
"File downloaded: {{title}}": "Fichier téléchargé : {{title}}",
"Deleted cloud copy of the file: {{title}}": "Suppression de la copie cloud du fichier : {{title}}",
"Failed to upload file: {{title}}": "Échec du téléchargement du fichier : {{title}}",
"Failed to download file: {{title}}": "Échec du téléchargement du fichier : {{title}}",
"Failed to delete cloud copy of the file: {{title}}": "Échec de la suppression de la copie cloud du fichier : {{title}}",
"{{percentage}}% used": "{{percentage}} % utilisés",
"Set PIN…": "Définir le PIN…",
"Require a 4-digit PIN to open Readest": "Exiger un PIN à 4 chiffres pour ouvrir Readest",
"Change PIN…": "Modifier le PIN…",
"Disable PIN…": "Désactiver le PIN…",
"Sync passphrase ready": "Phrase secrète de synchronisation prête",
"This permanently deletes the encrypted credentials we sync (e.g., OPDS catalog passwords) on every device. Local copies are preserved. You will need to re-enter the sync passphrase or set a new one. Continue?": "Cela supprime définitivement les identifiants chiffrés que nous synchronisons (par exemple, les mots de passe du catalogue OPDS) sur chaque appareil. Les copies locales sont conservées. Vous devrez ressaisir la phrase secrète de synchronisation ou en définir une nouvelle. Continuer ?",
"Sync passphrase forgotten — all encrypted fields cleared": "Phrase secrète de synchronisation oubliée — tous les champs chiffrés ont été effacés",
"Sync passphrase": "Phrase secrète de synchronisation",
"Set passphrase": "Définir la phrase secrète",
"Forgot passphrase": "Phrase secrète oubliée",
"Incorrect PIN": "PIN incorrect",
"App locked": "Application verrouillée",
"Enter your PIN": "Saisissez votre PIN",
"Readest is locked. Enter your 4-digit PIN to continue.": "Readest est verrouillé. Saisissez votre PIN à 4 chiffres pour continuer.",
"PIN code": "Code PIN",
"Forgetting your PIN locks you out of this device. You'll need to clear the app's data to reset it.": "Si vous oubliez votre PIN, vous serez verrouillé hors de cet appareil. Vous devrez effacer les données de l'application pour le réinitialiser.",
"Passphrase must be at least 8 characters": "La phrase secrète doit comporter au moins 8 caractères",
"Passphrases do not match": "Les phrases secrètes ne correspondent pas",
"Set sync passphrase": "Définir la phrase secrète de synchronisation",
"Enter sync passphrase": "Saisir la phrase secrète de synchronisation",
"A sync passphrase encrypts your sensitive fields (like OPDS catalog credentials) before they sync. We never see this passphrase. Pick something memorable — there is no recovery without it.": "Une phrase secrète de synchronisation chiffre vos champs sensibles (comme les identifiants du catalogue OPDS) avant qu'ils ne soient synchronisés. Nous ne voyons jamais cette phrase secrète. Choisissez quelque chose de mémorable — il n'y a pas de récupération sans elle.",
"Enter the sync passphrase you set on another device to decrypt your synced credentials.": "Saisissez la phrase secrète de synchronisation que vous avez définie sur un autre appareil pour déchiffrer vos identifiants synchronisés.",
"Confirm passphrase": "Confirmer la phrase secrète",
"Unlock": "Déverrouiller",
"Set PIN": "Définir le PIN",
"Change PIN": "Modifier le PIN",
"Disable PIN": "Désactiver le PIN",
"Enter your current PIN, then choose a new 4-digit PIN.": "Saisissez votre PIN actuel, puis choisissez un nouveau PIN à 4 chiffres.",
"Enter your current PIN to disable the app lock.": "Saisissez votre PIN actuel pour désactiver le verrouillage de l'application.",
"PIN must be {{length}} digits": "Le PIN doit comporter {{length}} chiffres",
"PINs do not match": "Les PIN ne correspondent pas",
"Current PIN": "PIN actuel",
"New PIN": "Nouveau PIN",
"Confirm new PIN": "Confirmer le nouveau PIN",
"Manage Sync": "Gérer la synchronisation",
"Imported book files and library metadata": "Fichiers de livres importés et métadonnées de la bibliothèque",
"Last-read position, bookmarks, and per-book preferences": "Dernière position de lecture, signets et préférences par livre",
"Highlights and notes": "Surlignages et notes",
"Fonts": "Polices",
"Custom font files": "Fichiers de polices personnalisées",
"Backgrounds": "Arrière-plans",
"Custom background textures": "Textures d'arrière-plan personnalisées",
"OPDS catalogs": "Catalogues OPDS",
"Saved catalog URLs and (encrypted) credentials": "URLs de catalogues enregistrées et identifiants (chiffrés)",
"Wrong sync passphrase — synced credentials could not be decrypted": "Phrase secrète de synchronisation incorrecte — les identifiants synchronisés n'ont pas pu être déchiffrés",
"Sync passphrase data on the server was reset. Re-encrypting your credentials under the new passphrase…": "Les données de phrase secrète de synchronisation sur le serveur ont été réinitialisées. Rechiffrement de vos identifiants avec la nouvelle phrase secrète…",
"Failed to decrypt synced credentials": "Impossible de déchiffrer les identifiants synchronisés",
"Imported dictionary bundles and settings": "Paquets de dictionnaires importés et paramètres",
"Choose what syncs across your devices. Disabling a category stops this device from sending or receiving rows of that kind. Anything already on the server is left alone, re-enabling resumes from where you stopped.": "Choisissez ce qui est synchronisé entre vos appareils. Désactiver une catégorie empêche cet appareil d'envoyer ou de recevoir des entrées de ce type. Ce qui est déjà sur le serveur reste intact, la réactivation reprend là où vous vous étiez arrêté.",
"Data Sync": "Synchronisation des données",
"Manage Fonts": "Gérer les polices",
"App settings": "Paramètres de lapplication",
"Theme, highlight colours, integrations (KOSync, Readwise, Hardcover), and dictionary order": "Thème, couleurs de surlignage, intégrations (KOSync, Readwise, Hardcover) et ordre des dictionnaires",
"Required while Dictionaries sync is enabled": "Requis tant que la synchronisation des dictionnaires est activée",
"Resets in {{hours}} hr {{minutes}} min": "Réinitialisation dans {{hours}} h {{minutes}} min",
"Pick a 4-digit PIN. You will need to enter it every time you open Readest. There is no way to recover a forgotten PIN. Clearing the app data is the only way to reset it.": "Choisissez un code PIN à 4 chiffres. Vous devrez le saisir chaque fois que vous ouvrirez Readest. Il n'existe aucun moyen de récupérer un code PIN oublié. Effacer les données de l'application est le seul moyen de le réinitialiser.",
"Credentials": "Identifiants",
"Tokens, usernames, and passwords for OPDS, KOReader, Hardcover, and Readwise. When disabled, credentials remain on this device only and are never uploaded.": "Jetons, noms d'utilisateur et mots de passe pour OPDS, KOReader, Hardcover et Readwise. Lorsqu'ils sont désactivés, les identifiants restent uniquement sur cet appareil et ne sont jamais téléversés.",
"Sensitive synced fields are encrypted before upload. Set a passphrase now or later when encryption is needed.": "Les champs sensibles synchronisés sont chiffrés avant l'envoi. Définissez une phrase de passe maintenant ou plus tard, lorsque le chiffrement sera nécessaire.",
"Saved to this account. You will be prompted for it when decrypting credentials.": "Enregistrée sur ce compte. Elle vous sera demandée lors du déchiffrement des identifiants.",
"Reading progress on this device differs from \"{{deviceName}}\".": "La progression de lecture sur cet appareil diffère de « {{deviceName}} ».",
"This device": "Cet appareil",
"Red": "Rouge",
"Yellow": "Jaune",
"Green": "Vert",
"Blue": "Bleu",
"Violet": "Violet",
"Edit color": "Modifier la couleur",
"Add custom color": "Ajouter une couleur personnalisée",
"Add label": "Ajouter une étiquette",
"Choose color": "Choisir une couleur",
"Connected to Hardcover. Last synced {{time}}.": "Connecté à Hardcover. Dernière synchronisation {{time}}.",
"Integrations": "Intégrations",
"Hardcover": "Hardcover",
"Connected to Readwise. Last synced {{time}}.": "Connecté à Readwise. Dernière synchronisation {{time}}.",
"Readwise": "Readwise",
"Browse and download books from online catalogs.": "Parcourez et téléchargez des livres depuis des catalogues en ligne.",
"Connected as {{user}}": "Connecté en tant que {{user}}",
"Not connected": "Non connecté",
"{{count}} catalog_one": "{{count}} catalogue",
"{{count}} catalog_many": "{{count}} catalogues",
"{{count}} catalog_other": "{{count}} catalogues",
"No catalogs": "Aucun catalogue",
"Connect Readest to external services for sync, highlights, and catalogs.": "Connectez Readest à des services externes pour la synchronisation, les surlignages et les catalogues.",
"Reading Sync": "Synchronisation de lecture",
"Content Sources": "Sources de contenu",
"Scroll tabs": "Faire défiler les onglets",
"Catalog actions": "Actions du catalogue",
"File Content": "Contenu du fichier",
"Start your library": "Commencez votre bibliothèque",
"Pick a book from your device to add it to your library.": "Choisissez un livre depuis votre appareil à ajouter à votre bibliothèque.",
"Drop a book anywhere on this window, or pick one from your computer.": "Déposez un livre n'importe où sur cette fenêtre, ou choisissez-en un depuis votre ordinateur.",
"Sign in to sync your library": "Connectez-vous pour synchroniser votre bibliothèque"
}
File diff suppressed because it is too large Load Diff
@@ -102,11 +102,9 @@
"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": "इस पुस्तक पर लागू करें",
@@ -173,9 +171,6 @@
"Token": "टोकन",
"Your OTP token": "आपका ओटीपी टोकन",
"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": "सामुदायिक सहायता",
@@ -188,7 +183,6 @@
"RTL Direction": "RTL दिशा",
"Maximum Column Height": "कॉलम की अधिकतम ऊँचाई",
"Maximum Column Width": "कॉलम की अधिकतम चौड़ाई",
"Continuous Scroll": "निरंतर स्क्रॉल",
"Fullscreen": "पूर्णस्क्रीन",
"No supported files found. Supported formats: {{formats}}": "कोई समर्थित फ़ाइलें नहीं मिलीं। समर्थित प्रारूप: {{formats}}",
"Drop to Import Books": "पुस्तकें आयात करने के लिए छोड़ें",
@@ -304,7 +298,6 @@
"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}} मिनट शेष अध्याय में",
@@ -338,11 +331,11 @@
"Fit": "फिट",
"Reset {{settings}}": "रीसेट {{settings}}",
"Reset Settings": "सेटिंग्स रीसेट करें",
"{{count}} pages left in chapter_one": "इस अध्याय में {{count}} पृष्ठ शेष हैं",
"{{count}} pages left in chapter_other": "इस अध्याय में {{count}} पृष्ठ शेष हैं",
"{{count}} pages left in chapter_one": "<1>इस अध्याय में </1><0>{{count}}</0><1> पृष्ठ शेष हैं</1>",
"{{count}} pages left in chapter_other": "<1>इस अध्याय में </1><0>{{count}}</0><1> पृष्ठ शेष हैं</1>",
"Show Remaining Pages": "शेष पृष्ठ दिखाएँ",
"Source Han Serif CN VF": "Source Han Serif",
"Huiwen-mincho": "Huiwen Mincho",
"Source Han Serif CN": "Source Han Serif",
"Huiwen-MinchoGBK": "Huiwen Mincho",
"KingHwa_OldSong": "KingHwa Song",
"Manage Subscription": "सदस्यता प्रबंधित करें",
"Coming Soon": "जल्द आ रहा है",
@@ -466,14 +459,12 @@
"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": "केवल परिवर्तन प्राप्त करें",
"Checksum Method": "चेकसम विधि",
"File Content (recommended)": "फाइल सामग्री (अनुशंसित)",
"File Name": "फाइल नाम",
"Device Name": "डिवाइस नाम",
"Connect to your KOReader Sync server.": "अपने KOReader सिंक सर्वर से कनेक्ट करें।",
@@ -484,10 +475,7 @@
"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}}%)",
@@ -602,11 +590,10 @@
"Size": "आकार",
"Cover": "कवर",
"Contain": "समाहित करें",
"{{number}} pages left in chapter": "इस अध्याय में {{number}} पृष्ठ शेष हैं",
"{{number}} pages left in chapter": "<1>इस अध्याय में </1><0>{{number}}</0><1> पृष्ठ शेष हैं</1>",
"Device": "डिवाइस",
"E-Ink Mode": "ई-इंक मोड",
"Highlight Colors": "हाइलाइट रंग",
"Auto Screen Brightness": "स्वचालित स्क्रीन ब्राइटनेस",
"Pagination": "पृष्ठांकन",
"Disable Double Tap": "डबल टैप अक्षम करें",
"Tap to Paginate": "पृष्ठांकन के लिए टैप करें",
@@ -641,7 +628,6 @@
"Strikethrough": "रेखांकित करें",
"Squiggly": "स्क्विगली",
"Outline": "आउटलाइन",
"Save Current Color": "वर्तमान रंग सहेजें",
"Quick Colors": "त्वरित रंग",
"Highlighter": "हाइलाइटर",
"Save Book Cover": "बुक कवर सहेजें",
@@ -699,9 +685,7 @@
"Validating...": "मान्य किया जा रहा है...",
"View All": "सभी देखें",
"Forward": "आगे",
"OPDS Catalog": "OPDS कैटलॉग",
"Home": "होम",
"Library": "लाइब्रेरी",
"{{count}} items_one": "{{count}} आइटम",
"{{count}} items_other": "{{count}} आइटम",
"Download completed": "डाउनलोड पूरा हुआ",
@@ -721,5 +705,712 @@
"Last": "अंतिम",
"Cannot Load Page": "पृष्ठ लोड नहीं किया जा सका",
"An error occurred": "एक त्रुटि हुई",
"Online Library": "ऑनलाइन लाइब्रेरी"
"Online Library": "ऑनलाइन लाइब्रेरी",
"URL must start with http:// or https://": "URL http:// या https:// से शुरू होना चाहिए",
"Title, Author, Tag, etc...": "शीर्षक, लेखक, टैग, आदि...",
"Query": "प्रश्न",
"Subject": "विषय",
"Enter {{terms}}": "{{terms}} दर्ज करें",
"No search results found": "कोई खोज परिणाम नहीं मिला",
"Failed to load OPDS feed: {{status}} {{statusText}}": "OPDS फ़ीड लोड करने में विफल: {{status}} {{statusText}}",
"Search in {{title}}": "{{title}} में खोजें",
"Manage Storage": "स्टोरेज प्रबंधित करें",
"Failed to load files": "फ़ाइलें लोड करने में विफल",
"Deleted {{count}} file(s)_one": "{{count}} फ़ाइल हटाई गई",
"Deleted {{count}} file(s)_other": "{{count}} फ़ाइलें हटाई गईं",
"Failed to delete {{count}} file(s)_one": "{{count}} फ़ाइल हटाने में विफल",
"Failed to delete {{count}} file(s)_other": "{{count}} फ़ाइलें हटाने में विफल",
"Failed to delete files": "फ़ाइलें हटाने में विफल",
"Total Files": "कुल फ़ाइलें",
"Total Size": "कुल आकार",
"Quota": "कोटा",
"Used": "उपयोग किया गया",
"Files": "फ़ाइलें",
"Search files...": "फ़ाइलें खोजें...",
"Newest First": "नवीनतम पहले",
"Oldest First": "सबसे पुराने पहले",
"Largest First": "सबसे बड़ी पहले",
"Smallest First": "सबसे छोटी पहले",
"Name A-Z": "नाम A-Z",
"Name Z-A": "नाम Z-A",
"{{count}} selected_one": "{{count}} चयनित",
"{{count}} selected_other": "{{count}} चयनित",
"Delete Selected": "चयनित हटाएँ",
"Created": "बनाई गई",
"No files found": "कोई फ़ाइल नहीं मिली",
"No files uploaded yet": "अभी तक कोई फ़ाइल अपलोड नहीं की गई",
"files": "फ़ाइलें",
"Page {{current}} of {{total}}": "पृष्ठ {{current}} / {{total}}",
"Are you sure to delete {{count}} selected file(s)?_one": "क्या आप वाकई {{count}} चयनित फ़ाइल हटाना चाहते हैं?",
"Are you sure to delete {{count}} selected file(s)?_other": "क्या आप वाकई {{count}} चयनित फ़ाइलें हटाना चाहते हैं?",
"Cloud Storage Usage": "क्लाउड स्टोरेज उपयोग",
"Rename Group": "समूह का नाम बदलें",
"From Directory": "निर्देशिका से",
"Successfully imported {{count}} book(s)_one": "सफलतापूर्वक 1 पुस्तक आयात की गई",
"Successfully imported {{count}} book(s)_other": "सफलतापूर्वक {{count}} पुस्तकों का आयात किया गया",
"Count": "गणना",
"Start Page": "प्रारंभ पृष्ठ",
"Search in OPDS Catalog...": "OPDS कैटलॉग में खोजें...",
"Please log in to use advanced TTS features": "उन्नत TTS सुविधाओं का उपयोग करने के लिए कृपया लॉग इन करें।",
"Word limit of 30 words exceeded.": "30 शब्दों की सीमा पार हो गई है।",
"Proofread": "प्रूफ़रीड",
"Current selection": "वर्तमान चयन",
"All occurrences in this book": "इस पुस्तक में सभी स्थान",
"All occurrences in your library": "आपकी लाइब्रेरी में सभी स्थान",
"Selected text:": "चयनित पाठ:",
"Replace with:": "से बदलें:",
"Enter text...": "पाठ दर्ज करें…",
"Case sensitive:": "अक्षर संवेदनशील:",
"Scope:": "क्षेत्र:",
"Selection": "चयन",
"Library": "लाइब्रेरी",
"Yes": "हाँ",
"No": "नहीं",
"Proofread Replacement Rules": "प्रूफ़रीड प्रतिस्थापन नियम",
"Selected Text Rules": "चयनित पाठ के नियम",
"No selected text replacement rules": "चयनित पाठ के लिए कोई प्रतिस्थापन नियम नहीं हैं",
"Book Specific Rules": "पुस्तक-विशिष्ट नियम",
"No book-level replacement rules": "पुस्तक स्तर पर कोई प्रतिस्थापन नियम नहीं हैं",
"Disable Quick Action": "त्वरित क्रिया अक्षम करें",
"Enable Quick Action on Selection": "चयन पर त्वरित क्रिया सक्षम करें",
"None": "कोई नहीं",
"Annotation Tools": "टिप्पणी उपकरण",
"Enable Quick Actions": "त्वरित क्रियाएँ सक्षम करें",
"Quick Action": "त्वरित क्रिया",
"Copy to Notebook": "नोटबुक में कॉपी करें",
"Copy text after selection": "पाठ कॉपी करें चयन के बाद",
"Highlight text after selection": "चयन के बाद पाठ हाइलाइट करें",
"Annotate text after selection": "चयन के बाद पाठ पर टिप्पणी करें",
"Search text after selection": "चयन के बाद पाठ खोजें",
"Look up text in dictionary after selection": "चयन के बाद शब्दकोश में पाठ देखें",
"Translate text after selection": "चयन के बाद पाठ का अनुवाद करें",
"Read text aloud after selection": "चयन के बाद पाठ को जोर से पढ़ें",
"Proofread text after selection": "चयन के बाद पाठ को प्रूफरीड करें",
"{{activeCount}} active, {{pendingCount}} pending": "{{activeCount}} सक्रिय, {{pendingCount}} लंबित",
"{{failedCount}} failed": "{{failedCount}} विफल",
"Waiting...": "प्रतीक्षा कर रहे हैं...",
"Failed": "विफल",
"Completed": "पूर्ण",
"Cancelled": "रद्द",
"Retry": "पुनः प्रयास करें",
"Active": "सक्रिय",
"Transfer Queue": "स्थानांतरण कतार",
"Upload All": "सभी अपलोड करें",
"Download All": "सभी डाउनलोड करें",
"Resume Transfers": "स्थानांतरण जारी रखें",
"Pause Transfers": "स्थानांतरण रोकें",
"Pending": "लंबित",
"No transfers": "कोई स्थानांतरण नहीं",
"Retry All": "सभी पुनः प्रयास करें",
"Clear Completed": "पूर्ण साफ़ करें",
"Clear Failed": "विफल साफ़ करें",
"Upload queued: {{title}}": "अपलोड कतार में: {{title}}",
"Download queued: {{title}}": "डाउनलोड कतार में: {{title}}",
"Book not found in library": "पुस्तकालय में पुस्तक नहीं मिली",
"Unknown error": "अज्ञात त्रुटि",
"Please log in to continue": "जारी रखने के लिए लॉगिन करें",
"Cloud File Transfers": "क्लाउड फ़ाइल स्थानांतरण",
"Show Search Results": "खोज परिणाम दिखाएं",
"Search results for '{{term}}'": "'{{term}}' के परिणाम",
"Close Search": "खोज बंद करें",
"Previous Result": "पिछला परिणाम",
"Next Result": "अगला परिणाम",
"Bookmarks": "बुकमार्क",
"Annotations": "टिप्पणियाँ",
"Show Results": "परिणाम दिखाएं",
"Clear search": "खोज साफ़ करें",
"Clear search history": "खोज इतिहास साफ़ करें",
"Tap to Toggle Footer": "फुटर टॉगल करने के लिए टैप करें",
"Show Current Time": "वर्तमान समय दिखाएं",
"Use 24 Hour Clock": "24 घंटे की घड़ी का उपयोग करें",
"Show Current Battery Status": "वर्तमान बैटरी स्थिति दिखाएं",
"Exported successfully": "सफलतापूर्वक निर्यात किया गया",
"Book exported successfully.": "पुस्तक सफलतापूर्वक निर्यात की गई।",
"Failed to export the book.": "पुस्तक निर्यात करने में विफल।",
"Export Book": "पुस्तक निर्यात करें",
"Whole word:": "पूरा शब्द:",
"Date Published": "प्रकाशन तिथि",
"Only for TTS:": "केवल TTS के लिए:",
"Uploaded": "अपलोड किया गया",
"Downloaded": "डाउनलोड किया गया",
"Deleted": "हटाया गया",
"Note:": "नोट:",
"Time:": "समय:",
"Format Options": "प्रारूप विकल्प",
"Export Date": "निर्यात तिथि",
"Chapter Titles": "अध्याय शीर्षक",
"Chapter Separator": "अध्याय विभाजक",
"Highlights": "हाइलाइट्स",
"Note Date": "नोट तिथि",
"Advanced": "उन्नत",
"Hide": "छिपाएं",
"Show": "दिखाएं",
"Use Custom Template": "कस्टम टेम्पलेट उपयोग करें",
"Export Template": "निर्यात टेम्पलेट",
"Template Syntax:": "टेम्पलेट सिंटैक्स:",
"Insert value": "मान डालें",
"Format date (locale)": "तिथि फ़ॉर्मेट करें (स्थानीय)",
"Format date (custom)": "तिथि फ़ॉर्मेट करें (कस्टम)",
"Conditional": "सशर्त",
"Loop": "लूप",
"Available Variables:": "उपलब्ध चर:",
"Book title": "पुस्तक शीर्षक",
"Book author": "पुस्तक लेखक",
"Export date": "निर्यात तिथि",
"Array of chapters": "अध्यायों की सूची",
"Chapter title": "अध्याय शीर्षक",
"Array of annotations": "एनोटेशन की सूची",
"Highlighted text": "हाइलाइट किया गया पाठ",
"Annotation note": "एनोटेशन नोट",
"Date Format Tokens:": "तिथि प्रारूप टोकन:",
"Year (4 digits)": "वर्ष (4 अंक)",
"Month (01-12)": "माह (01-12)",
"Day (01-31)": "दिन (01-31)",
"Hour (00-23)": "घंटा (00-23)",
"Minute (00-59)": "मिनट (00-59)",
"Second (00-59)": "सेकंड (00-59)",
"Show Source": "स्रोत दिखाएं",
"No content to preview": "पूर्वावलोकन के लिए कोई सामग्री नहीं",
"Export": "निर्यात करें",
"Set Timeout": "टाइमआउट सेट करें",
"Select Voice": "आवाज़ चुनें",
"Toggle Sticky Bottom TTS Bar": "स्थिर TTS बार टॉगल करें",
"Display what I'm reading on Discord": "Discord पर पढ़ रही किताब दिखाएं",
"Show on Discord": "Discord पर दिखाएं",
"Instant {{action}}": "तत्काल {{action}}",
"Instant {{action}} Disabled": "तत्काल {{action}} अक्षम",
"Annotation": "टिप्पणी",
"Reset Template": "टेम्पलेट रीसेट करें",
"Annotation style": "एनोटेशन शैली",
"Annotation color": "एनोटेशन रंग",
"Annotation time": "एनोटेशन समय",
"AI": "AI",
"Are you sure you want to re-index this book?": "क्या आप वाकई इस पुस्तक को फिर से इंडेक्स करना चाहते हैं?",
"Enable AI in Settings": "सेटिंग्स में AI सक्षम करें",
"Index This Book": "इस पुस्तक को इंडेक्स करें",
"Enable AI search and chat for this book": "इस पुस्तक के लिए AI खोज और चैट सक्षम करें",
"Start Indexing": "इंडेक्सिंग शुरू करें",
"Indexing book...": "पुस्तक इंडेक्स हो रही है...",
"Preparing...": "तैयारी हो रही है...",
"Delete this conversation?": "इस वार्तालाप को हटाएं?",
"No conversations yet": "अभी तक कोई वार्तालाप नहीं",
"Start a new chat to ask questions about this book": "इस पुस्तक के बारे में प्रश्न पूछने के लिए नई चैट शुरू करें",
"Rename": "नाम बदलें",
"New Chat": "नई चैट",
"Chat": "चैट",
"Please enter a model ID": "कृपया मॉडल ID दर्ज करें",
"Model not available or invalid": "मॉडल उपलब्ध नहीं या अमान्य है",
"Failed to validate model": "मॉडल सत्यापित करने में विफल",
"Couldn't connect to Ollama. Is it running?": "Ollama से कनेक्ट नहीं हो सका। क्या यह चल रहा है?",
"Invalid API key or connection failed": "अमान्य API कुंजी या कनेक्शन विफल",
"Connection failed": "कनेक्शन विफल",
"AI Assistant": "AI सहायक",
"Enable AI Assistant": "AI सहायक सक्षम करें",
"Provider": "प्रदाता",
"Ollama (Local)": "Ollama (स्थानीय)",
"AI Gateway (Cloud)": "AI गेटवे (क्लाउड)",
"Ollama Configuration": "Ollama कॉन्फ़िगरेशन",
"Refresh Models": "मॉडल रीफ्रेश करें",
"AI Model": "AI मॉडल",
"No models detected": "कोई मॉडल नहीं मिला",
"AI Gateway Configuration": "AI गेटवे कॉन्फ़िगरेशन",
"Choose from a selection of high-quality, economical AI models. You can also bring your own model by selecting \"Custom Model\" below.": "उच्च-गुणवत्ता, किफायती AI मॉडल के चयन में से चुनें। आप नीचे \"कस्टम मॉडल\" का चयन करके अपना मॉडल भी उपयोग कर सकते हैं।",
"API Key": "API कुंजी",
"Get Key": "कुंजी प्राप्त करें",
"Model": "मॉडल",
"Custom Model...": "कस्टम मॉडल...",
"Custom Model ID": "कस्टम मॉडल ID",
"Validate": "सत्यापित करें",
"Model available": "मॉडल उपलब्ध",
"Connection": "कनेक्शन",
"Test Connection": "कनेक्शन परीक्षण",
"Connected": "कनेक्टेड",
"Custom Colors": "कस्टम रंग",
"Color E-Ink Mode": "कलर ई-इंक मोड",
"Reading Ruler": "पठन रूलर",
"Enable Reading Ruler": "पठन रूलर सक्षम करें",
"Lines to Highlight": "हाइलाइट करने के लिए पंक्तियाँ",
"Ruler Color": "रूलर का रंग",
"Command Palette": "कमांड पैलेट",
"Search settings and actions...": "सेटिंग और क्रियाएं खोजें...",
"No results found for": "के लिए कोई परिणाम नहीं मिला",
"Type to search settings and actions": "सेटिंग और क्रियाएं खोजने के लिए टाइप करें",
"Recent": "हालिया",
"navigate": "नेविगेट",
"select": "चुनें",
"close": "बंद करें",
"Search Settings": "सेटिंग खोजें",
"Page Margins": "पेज मार्जिन",
"AI Provider": "AI प्रदाता",
"Ollama URL": "Ollama URL",
"Ollama Model": "Ollama मॉडल",
"AI Gateway Model": "AI गेटवे मॉडल",
"Actions": "क्रियाएं",
"Navigation": "नेविगेशन",
"Set status for {{count}} book(s)_one": "{{count}} किताब की स्थिति सेट करें",
"Set status for {{count}} book(s)_other": "{{count}} किताबों की स्थिति सेट करें",
"Mark as Unread": "अपठित के रूप में चिह्नित करें",
"Mark as Finished": "समाप्त के रूप में चिह्नित करें",
"Finished": "समाप्त",
"Unread": "अपठित",
"Clear Status": "स्थिति साफ़ करें",
"Status": "स्थिति",
"Loading": "लोड हो रहा है...",
"Exit Paragraph Mode": "पैराग्राफ मोड से बाहर निकलें",
"Paragraph Mode": "पैराग्राफ मोड",
"Embedding Model": "एंबेडिंग मॉडल",
"{{count}} book(s) synced_one": "{{count}} पुस्तक सिंक की गई",
"{{count}} book(s) synced_other": "{{count}} पुस्तकें सिंक की गईं",
"Unable to start RSVP": "RSVP शुरू करने में असमर्थ",
"RSVP not supported for PDF": "PDF के लिए RSVP समर्थित नहीं है",
"Select Chapter": "अध्याय चुनें",
"Context": "संदर्भ",
"Ready": "तैयार",
"Chapter Progress": "अध्याय प्रगति",
"words": "शब्द",
"{{time}} left": "{{time}} शेष",
"Reading progress": "पठन प्रगति",
"Skip back 15 words": "15 शब्द पीछे",
"Back 15 words (Shift+Left)": "15 शब्द पीछे (Shift+Left)",
"Pause (Space)": "विराम (Space)",
"Play (Space)": "चलाएं (Space)",
"Skip forward 15 words": "15 शब्द आगे",
"Forward 15 words (Shift+Right)": "15 शब्द आगे (Shift+Right)",
"Decrease speed": "गति कम करें",
"Slower (Left/Down)": "धीमी (Left/Down)",
"Increase speed": "गति बढ़ाएं",
"Faster (Right/Up)": "तेज़ (Right/Up)",
"Start RSVP Reading": "RSVP पठन शुरू करें",
"Choose where to start reading": "चुनें कि कहाँ से पढ़ना शुरू करना है",
"From Chapter Start": "अध्याय के आरंभ से",
"Start reading from the beginning of the chapter": "अध्याय की शुरुआत से पढ़ना शुरू करें",
"Resume": "फिर से शुरू करें",
"Continue from where you left off": "वहीं से जारी रखें जहाँ आपने छोड़ा था",
"From Current Page": "वर्तमान पृष्ठ से",
"Start from where you are currently reading": "जहाँ आप अभी पढ़ रहे हैं वहीं से शुरू करें",
"From Selection": "चयन से",
"Speed Reading Mode": "त्वरित पठन मोड",
"Scroll left": "बाएं स्क्रॉल करें",
"Scroll right": "दाएं स्क्रॉल करें",
"Library Sync Progress": "पुस्तकालय सिंक प्रगति",
"Back to library": "लाइब्रेरी पर वापस जाएं",
"Group by...": "इसके द्वारा समूहबद्ध करें...",
"Export as Plain Text": "प्लेन टेक्स्ट के रूप में एक्सपोर्ट करें",
"Export as Markdown": "Markdown के रूप में एक्सपोर्ट करें",
"Show Page Navigation Buttons": "नेविगेशन बटन",
"Page {{number}}": "पृष्ठ {{number}}",
"highlight": "हाइलाइट",
"underline": "अंडरलाइन",
"squiggly": "टेढ़ी-मेढ़ी रेखा",
"red": "लाल",
"violet": "बैंगनी",
"blue": "नीला",
"green": "हरा",
"yellow": "पीला",
"Select {{style}} style": "{{style}} शैली चुनें",
"Select {{color}} color": "{{color}} रंग चुनें",
"Close Book": "किताब बंद करें",
"Speed Reading": "तीव्र पठन",
"Close Speed Reading": "तीव्र पठन बंद करें",
"Authors": "लेखक",
"Books": "पुस्तकें",
"Groups": "समूह",
"Back to TTS Location": "टीटीएस स्थान पर वापस जाएं",
"Metadata": "मेटाडाटा",
"Image viewer": "छवि दर्शक",
"Previous Image": "पिछली छवि",
"Next Image": "अगली छवि",
"Zoomed": "ज़ूम किया गया",
"Zoom level": "ज़ूम स्तर",
"Table viewer": "तालिका दर्शक",
"Unable to connect to Readwise. Please check your network connection.": "Readwise से कनेक्ट करने में असमर्थ। कृपया अपना नेटवर्क कनेक्शन जांचें।",
"Invalid Readwise access token": "अमान्य Readwise एक्सेस टोकन",
"Disconnected from Readwise": "Readwise से डिस्कनेक्ट हो गया",
"Never": "कभी नहीं",
"Sync Enabled": "सिंक सक्षम",
"Disconnect": "डिस्कनेक्ट करें",
"Connect your Readwise account to sync highlights.": "हाइलाइट्स सिंक करने के लिए अपना Readwise खाता कनेक्ट करें।",
"Get your access token at": "अपना एक्सेस टोकन यहाँ प्राप्त करें",
"Access Token": "एक्सेस टोकन",
"Paste your Readwise access token": "अपना Readwise एक्सेस टोकन पेस्ट करें",
"Readwise Sync": "Readwise सिंक",
"Push Highlights": "हाइलाइट्स भेजें",
"Highlights synced to Readwise": "हाइलाइट्स Readwise में सिंक हो गए",
"Readwise sync failed: no internet connection": "Readwise सिंक विफल रहा: कोई इंटरनेट कनेक्शन नहीं",
"Readwise sync failed: {{error}}": "Readwise सिंक विफल रहा: {{error}}",
"System Screen Brightness": "सिस्टम स्क्रीन चमक",
"Page:": "पृष्ठ:",
"Page: {{number}}": "पृष्ठ: {{number}}",
"Annotation page number": "व्याख्या पृष्ठ संख्या",
"Translating...": "अनुवाद हो रहा है...",
"Show Battery Percentage": "बैटरी प्रतिशत दिखाएं",
"Hide Scrollbar": "स्क्रॉलबार छुपाएँ",
"Skip to last reading position": "अंतिम पढ़ने की स्थिति पर जाएँ",
"Show context": "संदर्भ दिखाएँ",
"Hide context": "संदर्भ छुपाएँ",
"Decrease font size": "फ़ॉन्ट आकार घटाएँ",
"Increase font size": "फ़ॉन्ट आकार बढ़ाएँ",
"Focus": "फ़ोकस",
"Theme color": "थीम रंग",
"Import failed": "आयात विफल",
"Available Formatters:": "उपलब्ध फ़ॉर्मेटर:",
"Format date": "तारीख फ़ॉर्मेट करें",
"Markdown block quote (> per line)": "Markdown ब्लॉक कोट (> प्रति पंक्ति)",
"Newlines to <br>": "नई पंक्तियाँ <br> में",
"Change case": "केस बदलें",
"Trim whitespace": "रिक्त स्थान हटाएँ",
"Truncate to n characters": "n अक्षरों तक छोटा करें",
"Replace text": "टेक्स्ट बदलें",
"Fallback value": "फ़ॉलबैक मान",
"Get length": "लंबाई प्राप्त करें",
"First/last element": "पहला/अंतिम तत्व",
"Join array": "ऐरे जोड़ें",
"Backup failed: {{error}}": "बैकअप विफल: {{error}}",
"Select Backup": "बैकअप चुनें",
"Restore failed: {{error}}": "पुनर्स्थापना विफल: {{error}}",
"Backup & Restore": "बैकअप और पुनर्स्थापना",
"Create a backup of your library or restore from a previous backup. Restoring will merge with your current library.": "अपनी लाइब्रेरी का बैकअप बनाएं या पिछले बैकअप से पुनर्स्थापित करें। पुनर्स्थापना आपकी वर्तमान लाइब्रेरी के साथ मर्ज होगी।",
"Backup Library": "लाइब्रेरी बैकअप",
"Restore Library": "लाइब्रेरी पुनर्स्थापित करें",
"Creating backup...": "बैकअप बनाया जा रहा है...",
"Restoring library...": "लाइब्रेरी पुनर्स्थापित हो रही है...",
"{{current}} of {{total}} items": "{{current}} / {{total}} आइटम",
"Backup completed successfully!": "बैकअप सफलतापूर्वक पूर्ण!",
"Restore completed successfully!": "पुनर्स्थापना सफलतापूर्वक पूर्ण!",
"Your library has been saved to the selected location.": "आपकी लाइब्रेरी चयनित स्थान पर सहेजी गई है।",
"{{added}} books added, {{updated}} books updated.": "{{added}} पुस्तकें जोड़ी गईं, {{updated}} पुस्तकें अपडेट की गईं।",
"Operation failed": "ऑपरेशन विफल",
"Loading library...": "लाइब्रेरी लोड हो रही है...",
"{{count}} books refreshed_one": "{{count}} पुस्तक रिफ्रेश हुई",
"{{count}} books refreshed_other": "{{count}} पुस्तकें रिफ्रेश हुईं",
"Failed to refresh metadata": "मेटाडेटा रिफ्रेश विफल",
"Refresh Metadata": "मेटाडेटा रिफ्रेश करें",
"Keyboard Shortcuts": "कीबोर्ड शॉर्टकट",
"View all keyboard shortcuts": "सभी कीबोर्ड शॉर्टकट देखें",
"Switch Sidebar Tab": "साइडबार टैब बदलें",
"Toggle Notebook": "नोटबुक दिखाएँ/छुपाएँ",
"Search in Book": "पुस्तक में खोजें",
"Toggle Scroll Mode": "स्क्रॉल मोड टॉगल करें",
"Toggle Select Mode": "चयन मोड टॉगल करें",
"Toggle Bookmark": "बुकमार्क टॉगल करें",
"Toggle Text to Speech": "टेक्स्ट टू स्पीच टॉगल करें",
"Play / Pause TTS": "TTS चलाएँ / रोकें",
"Toggle Paragraph Mode": "अनुच्छेद मोड टॉगल करें",
"Highlight Selection": "चयन हाइलाइट करें",
"Underline Selection": "चयन अंडरलाइन करें",
"Annotate Selection": "चयन पर टिप्पणी करें",
"Search Selection": "चयन खोजें",
"Copy Selection": "चयन कॉपी करें",
"Translate Selection": "चयन का अनुवाद करें",
"Dictionary Lookup": "शब्दकोश में खोजें",
"Read Aloud Selection": "चयन को ज़ोर से पढ़ें",
"Proofread Selection": "चयन की प्रूफरीडिंग करें",
"Open Settings": "सेटिंग्स खोलें",
"Open Command Palette": "कमांड पैलेट खोलें",
"Show Keyboard Shortcuts": "कीबोर्ड शॉर्टकट दिखाएँ",
"Open Books": "पुस्तकें खोलें",
"Toggle Fullscreen": "पूर्ण स्क्रीन टॉगल करें",
"Close Window": "विंडो बंद करें",
"Quit App": "ऐप से बाहर निकलें",
"Go Left / Previous Page": "बाएँ जाएँ / पिछला पृष्ठ",
"Go Right / Next Page": "दाएँ जाएँ / अगला पृष्ठ",
"Go Up": "ऊपर जाएँ",
"Go Down": "नीचे जाएँ",
"Previous Chapter": "पिछला अध्याय",
"Next Chapter": "अगला अध्याय",
"Scroll Half Page Down": "आधा पृष्ठ नीचे स्क्रॉल करें",
"Scroll Half Page Up": "आधा पृष्ठ ऊपर स्क्रॉल करें",
"Save Note": "नोट सहेजें",
"Single Section Scroll": "एकल अनुभाग स्क्रॉल",
"General": "सामान्य",
"Text to Speech": "पाठ से वाक्",
"Zoom": "ज़ूम",
"Window": "विंडो",
"Your Bookshelf": "आपकी किताबों की अलमारी",
"TTS": "पाठ से वाक्",
"Media Info": "मीडिया जानकारी",
"Update Frequency": "अपडेट आवृत्ति",
"Every Sentence": "हर वाक्य",
"Every Paragraph": "हर पैराग्राफ",
"Every Chapter": "हर अध्याय",
"TTS Media Info Update Frequency": "TTS मीडिया जानकारी अपडेट आवृत्ति",
"Select reading speed": "पढ़ने की गति चुनें",
"Drag to seek": "खोजने के लिए खींचें",
"Punctuation Delay": "विराम चिह्न विलंब",
"Please confirm that this OPDS connection will be proxied through Readest servers on the web app before continuing.": "कृपया जारी रखने से पहले पुष्टि करें कि यह OPDS कनेक्शन वेब ऐप पर Readest सर्वर के माध्यम से प्रॉक्सी किया जाएगा।",
"Custom Headers (optional)": "कस्टम हेडर (वैकल्पिक)",
"Add one header per line using \"Header-Name: value\".": "\"Header-Name: value\" का उपयोग करके प्रति पंक्ति एक हेडर जोड़ें।",
"I understand this OPDS connection will be proxied through Readest servers on the web app. If I do not trust Readest with these credentials or headers, I should use the native app instead.": "मैं समझता/समझती हूँ कि यह OPDS कनेक्शन वेब ऐप पर Readest सर्वर के माध्यम से प्रॉक्सी किया जाएगा। यदि मुझे इन क्रेडेंशियल या हेडर के साथ Readest पर भरोसा नहीं है, तो मुझे नेटिव ऐप का उपयोग करना चाहिए।",
"Unable to connect to Hardcover. Please check your network connection.": "Hardcover से कनेक्ट करने में असमर्थ। कृपया अपना नेटवर्क कनेक्शन जाँचें।",
"Invalid Hardcover API token": "अमान्य Hardcover API टोकन",
"Disconnected from Hardcover": "Hardcover से डिस्कनेक्ट हो गया",
"Connect your Hardcover account to sync reading progress and notes.": "पढ़ने की प्रगति और नोट्स सिंक करने के लिए अपना Hardcover खाता कनेक्ट करें।",
"Get your API token from hardcover.app → Settings → API.": "hardcover.app → सेटिंग्स → API से अपना API टोकन प्राप्त करें।",
"API Token": "API टोकन",
"Paste your Hardcover API token": "अपना Hardcover API टोकन पेस्ट करें",
"Hardcover Sync": "Hardcover सिंक",
"Push Notes": "नोट्स भेजें",
"No annotations or excerpts to sync for this book.": "इस पुस्तक के लिए सिंक करने हेतु कोई एनोटेशन या अंश नहीं हैं।",
"Configure Hardcover in Settings first.": "पहले सेटिंग्स में Hardcover कॉन्फ़िगर करें।",
"No new Hardcover note changes to sync.": "सिंक करने के लिए कोई नया Hardcover नोट परिवर्तन नहीं है।",
"Hardcover synced: {{inserted}} new, {{updated}} updated, {{skipped}} unchanged": "Hardcover सिंक हुआ: {{inserted}} नए, {{updated}} अपडेटेड, {{skipped}} अपरिवर्तित",
"Hardcover notes sync failed: {{error}}": "Hardcover नोट्स सिंक विफल: {{error}}",
"Reading progress synced to Hardcover": "पढ़ने की प्रगति Hardcover से सिंक हो गई",
"Hardcover progress sync failed: {{error}}": "Hardcover प्रगति सिंक विफल: {{error}}",
"ISBN": "ISBN",
"Keep existing source identifier": "मौजूदा स्रोत पहचानकर्ता रखें",
"Toggle Toolbar": "टूलबार टॉगल करें",
"Split Hyphens": "हाइफ़न विभाजित करें",
"Apply Theme Colors to PDF": "PDF पर थीम रंग लागू करें",
"Name": "नाम",
"Remove": "हटाएँ",
"Edit OPDS Catalog": "OPDS कैटलॉग संपादित करें",
"Save Changes": "परिवर्तन सहेजें",
"Use Book Layout": "पुस्तक का लेआउट उपयोग करें",
"End of this section. Continue to the next.": "इस अनुभाग का अंत। अगले पर जारी रखें।",
"Auto-download": "स्वचालित डाउनलोड",
"Auto-download new items": "नई आइटम स्वचालित रूप से डाउनलोड करें",
"Automatically download new publications when the app syncs": "ऐप सिंक होने पर नए प्रकाशन स्वचालित रूप से डाउनलोड करें",
"{{count}} new item(s) downloaded from OPDS_one": "OPDS से {{count}} नया आइटम डाउनलोड किया गया",
"{{count}} new item(s) downloaded from OPDS_other": "OPDS से {{count}} नई आइटम डाउनलोड की गईं",
"Failed to sync {{count}} OPDS catalog(s)_one": "{{count}} OPDS कैटलॉग सिंक करने में विफल",
"Failed to sync {{count}} OPDS catalog(s)_other": "{{count}} OPDS कैटलॉग सिंक करने में विफल",
"Last synced {{when}}": "अंतिम सिंक्रनाइज़ेशन {{when}}",
"Failed downloads": "असफल डाउनलोड",
"No failed downloads": "कोई असफल डाउनलोड नहीं",
"Attempts: {{count}}_one": "प्रयास: {{count}}",
"Attempts: {{count}}_other": "प्रयास: {{count}}",
"Skip": "छोड़ें",
"Skip all": "सब छोड़ें",
"Retry all": "सब पुनः प्रयास करें",
"{{count}} failed_one": "{{count}} असफल",
"{{count}} failed_other": "{{count}} असफल",
"(none)": "(कोई नहीं)",
"Identifiers": "पहचानकर्ता",
"Read (Stream)": "पढ़ें (स्ट्रीम)",
"Failed to start stream": "स्ट्रीम प्रारंभ करने में विफल",
"No dictionaries enabled": "कोई शब्दकोश सक्षम नहीं है",
"Enable a dictionary in Settings → Language → Dictionaries.": "सेटिंग्स → भाषा → शब्दकोश में जाकर शब्दकोश सक्षम करें।",
"Wiktionary": "विक्षनरी",
"Drag to reorder": "पुनः क्रमित करने के लिए खींचें",
"Built-in": "अंतर्निर्मित",
"Bundle is missing on this device. Re-import to use it.": "इस डिवाइस पर बंडल मौजूद नहीं है। उपयोग के लिए पुनः आयात करें।",
"This dictionary format is not supported.": "यह शब्दकोश प्रारूप समर्थित नहीं है।",
"MDict": "MDict",
"StarDict": "StarDict",
"Imported {{count}} dictionary_one": "{{count}} शब्दकोश आयात किया गया",
"Imported {{count}} dictionary_other": "{{count}} शब्दकोश आयात किए गए",
"Skipped incomplete bundles: {{names}}": "अधूरे बंडल छोड़े गए: {{names}}",
"Failed to import dictionary: {{message}}": "शब्दकोश आयात विफल: {{message}}",
"Dictionaries": "शब्दकोश",
"Delete Dictionary": "शब्दकोश हटाएँ",
"Importing…": "आयात हो रहा है…",
"Import Dictionary": "शब्दकोश आयात करें",
"No dictionaries available.": "कोई शब्दकोश उपलब्ध नहीं है।",
"StarDict bundles need .ifo, .idx, and .dict.dz files (.syn optional).": "StarDict बंडल को .ifo, .idx और .dict.dz फ़ाइलें चाहिए (.syn वैकल्पिक)।",
"Select all the bundle files together when importing.": "आयात करते समय बंडल की सभी फ़ाइलें एक साथ चुनें।",
"Manage Dictionaries": "शब्दकोश प्रबंधित करें",
"Select Dictionary Files": "शब्दकोश फ़ाइलें चुनें",
"Read on Wikipedia →": "विकिपीडिया पर पढ़ें →",
"This link can't be opened": "यह लिंक नहीं खोला जा सकता",
"The annotation link is missing required information. The original link may have been truncated.": "एनोटेशन लिंक में आवश्यक जानकारी नहीं है। मूल लिंक छोटा हो गया हो सकता है।",
"Go to Readest": "Readest पर जाएँ",
"Open-source ebook reader for everyone, on every device.": "हर किसी के लिए, हर डिवाइस पर — ओपन-सोर्स ईबुक रीडर।",
"Open in Readest": "Readest में खोलें",
"If Readest didn't open automatically, choose an option below:": "अगर Readest स्वतः नहीं खुला, तो नीचे से कोई विकल्प चुनें:",
"Continue reading where you left off.": "जहाँ छोड़ा था, वहीं से पढ़ना जारी रखें।",
"Readest logo": "Readest का लोगो",
"Opening Readest...": "Readest खुल रहा है…",
"Open in Readest app": "Readest ऐप में खोलें",
"Continue in browser": "ब्राउज़र में जारी रखें",
"Don't have Readest?": "Readest नहीं है?",
"Book not in your library": "यह किताब आपकी लाइब्रेरी में नहीं है",
"Share Book": "पुस्तक साझा करें",
"Could not create share link": "साझा लिंक नहीं बनाया जा सका",
"Link copied": "लिंक कॉपी किया गया",
"Could not copy link": "लिंक कॉपी नहीं किया जा सका",
"Share revoked": "साझा रद्द किया गया",
"Could not revoke share": "साझा रद्द नहीं किया जा सका",
"Uploading book…": "पुस्तक अपलोड हो रही है…",
"Generating…": "बना रहा है…",
"Generate share link": "साझा लिंक बनाएँ",
"Share URL": "साझा URL",
"Copy link": "लिंक कॉपी करें",
"Copied": "कॉपी किया गया",
"Share via…": "के माध्यम से साझा करें…",
"Expires {{date}}": "{{date}} को समाप्त",
"Revoking…": "रद्द कर रहा है…",
"Revoke share": "साझा रद्द करें",
"Sign in to share books": "पुस्तकें साझा करने के लिए साइन इन करें",
"Expiring soon": "जल्द ही समाप्त होगा",
"Missing share token": "साझा टोकन गायब है",
"Could not add to your library": "आपकी लाइब्रेरी में जोड़ा नहीं जा सका",
"This share link is no longer available": "यह साझा लिंक अब उपलब्ध नहीं है",
"The original link may have expired or been revoked.": "मूल लिंक समाप्त हो गया हो सकता है या रद्द कर दिया गया हो।",
"Get Readest": "Readest प्राप्त करें",
"Loading shared book…": "साझा पुस्तक लोड हो रही है…",
"Adding…": "जोड़ रहा है…",
"Add to my library": "मेरी लाइब्रेरी में जोड़ें",
"Could not load your shares": "आपके साझा लोड नहीं किए जा सके",
"Revoked": "रद्द",
"Expired": "समाप्त",
"Shared books": "साझा पुस्तकें",
"You haven't shared any books yet": "आपने अभी तक कोई पुस्तक साझा नहीं की है",
"Open a book and tap Share to send it to a friend.": "किसी पुस्तक को खोलें और मित्र को भेजने के लिए साझा पर टैप करें।",
"{{count}} active_one": "{{count}} सक्रिय",
"{{count}} active_other": "{{count}} सक्रिय",
"{{count}} downloads_one": "{{count}} डाउनलोड",
"{{count}} downloads_other": "{{count}} डाउनलोड",
"starts at saved page": "सहेजे गए पृष्ठ से शुरू",
"More actions": "अधिक क्रियाएँ",
"Loading…": "लोड हो रहा है…",
"Load more": "और लोड करें",
"Could not load shared book": "साझा पुस्तक लोड नहीं की जा सकी",
"The share link is missing required information.": "साझा लिंक में आवश्यक जानकारी नहीं है।",
"Please check your connection and try again.": "कृपया अपना कनेक्शन जांचें और पुनः प्रयास करें।",
"Sign in to import shared books": "साझा पुस्तकें आयात करने के लिए साइन इन करें",
"Already in your library": "पहले से ही आपकी लाइब्रेरी में है",
"Added to your library": "आपकी लाइब्रेरी में जोड़ा गया",
"Could not import shared book": "साझा पुस्तक आयात नहीं की जा सकी",
"Manage Shared Links": "साझा लिंक प्रबंधित करें",
"Expires in": "समाप्त होगा",
"{{count}} days_one": "1 दिन",
"{{count}} days_other": "{{count}} दिन",
"Expires in {{count}} days_one": "1 दिन में समाप्त",
"Expires in {{count}} days_other": "{{count}} दिनों में समाप्त",
"Expires in {{count}} hours_one": "1 घंटे में समाप्त",
"Expires in {{count}} hours_other": "{{count}} घंटों में समाप्त",
"Open in app": "ऐप में खोलें",
"Shared with you": "आपके साथ साझा किया गया",
"Downloading… {{percent}}%": "डाउनलोड हो रहा है… {{percent}}%",
"Import progress": "आयात प्रगति",
"Share reading progress": "पढ़ने की प्रगति साझा करें",
"Includes your reading progress": "आपकी पढ़ने की प्रगति शामिल है",
"Synced {{time}}": "{{time}} सिंक हुआ",
"Sync Info": "सिंक जानकारी",
"Last Synced": "अंतिम सिंक",
"DICT": "DICT",
"Slob": "Slob",
"DICT bundles need a .index file and a .dict.dz file.": "DICT बंडल को एक .index फ़ाइल और एक .dict.dz फ़ाइल की आवश्यकता होती है।",
"Slob bundles need a .slob file.": "Slob बंडल को एक .slob फ़ाइल की आवश्यकता होती है।",
"URL template must start with http(s):// and contain %WORD%.": "URL टेम्पलेट http(s):// से शुरू होना चाहिए और उसमें %WORD% होना चाहिए।",
"Web": "वेब",
"Add Web Search": "वेब खोज जोड़ें",
"Edit Web Search": "वेब खोज संपादित करें",
"e.g. Google": "उदा. Google",
"URL Template": "URL टेम्पलेट",
"Use %WORD% where the looked-up word should appear.": "%WORD% का उपयोग वहां करें जहां खोजा गया शब्द दिखाई देना चाहिए।",
"Open the search result in your browser:": "अपने ब्राउज़र में खोज परिणाम खोलें:",
"Open in {{name}}": "{{name}} में खोलें",
"Book Fingerprint": "पुस्तक फिंगरप्रिंट",
"Search the web": "वेब पर खोजें",
"App deeplink (readest://)": "ऐप डीप लिंक (readest://)",
"Universal web link (https://)": "सार्वभौमिक वेब लिंक (https://)",
"Name cannot be empty.": "नाम खाली नहीं हो सकता।",
"Replaced {{count}} existing dictionary_one": "{{count}} मौजूदा शब्दकोश बदला गया",
"Replaced {{count}} existing dictionary_other": "{{count}} मौजूदा शब्दकोश बदले गए",
"Cancel Edit": "संपादन रद्द करें",
"Edit Dictionary": "शब्दकोश संपादित करें",
"MDict bundles use .mdx files; companion .mdd and .css files are optional.": "MDict बंडल .mdx फ़ाइलों का उपयोग करते हैं; साथ की .mdd और .css फ़ाइलें वैकल्पिक हैं।",
"Dictionary name": "शब्दकोश का नाम",
"This audio can't play here — the dictionary uses an outdated format. Try one with Opus, MP3, or WAV audio.": "यह ऑडियो यहाँ नहीं चलाया जा सकता — शब्दकोश पुराने प्रारूप का उपयोग करता है। Opus, MP3 या WAV ऑडियो वाला कोई आज़माएँ।",
"File uploaded: {{title}}": "फ़ाइल अपलोड की गई: {{title}}",
"File downloaded: {{title}}": "फ़ाइल डाउनलोड की गई: {{title}}",
"Deleted cloud copy of the file: {{title}}": "फ़ाइल की क्लाउड प्रति हटाई गई: {{title}}",
"Failed to upload file: {{title}}": "फ़ाइल अपलोड करने में विफल: {{title}}",
"Failed to download file: {{title}}": "फ़ाइल डाउनलोड करने में विफल: {{title}}",
"Failed to delete cloud copy of the file: {{title}}": "फ़ाइल की क्लाउड प्रति हटाने में विफल: {{title}}",
"{{percentage}}% used": "{{percentage}}% उपयोग किया गया",
"Set PIN…": "PIN सेट करें…",
"Require a 4-digit PIN to open Readest": "Readest खोलने के लिए 4-अंकों का PIN आवश्यक करें",
"Change PIN…": "PIN बदलें…",
"Disable PIN…": "PIN अक्षम करें…",
"Sync passphrase ready": "सिंक पासफ़्रेज़ तैयार",
"This permanently deletes the encrypted credentials we sync (e.g., OPDS catalog passwords) on every device. Local copies are preserved. You will need to re-enter the sync passphrase or set a new one. Continue?": "यह हर डिवाइस पर हमारे सिंक किए गए एन्क्रिप्टेड क्रेडेंशियल (जैसे, OPDS कैटलॉग पासवर्ड) को स्थायी रूप से हटा देता है। स्थानीय प्रतियां सुरक्षित रहती हैं। आपको सिंक पासफ़्रेज़ फिर से दर्ज करना होगा या एक नया सेट करना होगा। जारी रखें?",
"Sync passphrase forgotten — all encrypted fields cleared": "सिंक पासफ़्रेज़ भूल गए — सभी एन्क्रिप्टेड फ़ील्ड हटा दिए गए",
"Sync passphrase": "सिंक पासफ़्रेज़",
"Set passphrase": "पासफ़्रेज़ सेट करें",
"Forgot passphrase": "पासफ़्रेज़ भूल गए",
"Incorrect PIN": "गलत PIN",
"App locked": "ऐप लॉक है",
"Enter your PIN": "अपना PIN दर्ज करें",
"Readest is locked. Enter your 4-digit PIN to continue.": "Readest लॉक है। जारी रखने के लिए अपना 4-अंकों का PIN दर्ज करें।",
"PIN code": "PIN कोड",
"Forgetting your PIN locks you out of this device. You'll need to clear the app's data to reset it.": "PIN भूलने पर आप इस डिवाइस से बाहर हो जाएंगे। इसे रीसेट करने के लिए आपको ऐप का डेटा मिटाना होगा।",
"Passphrase must be at least 8 characters": "पासफ़्रेज़ कम से कम 8 वर्णों का होना चाहिए",
"Passphrases do not match": "पासफ़्रेज़ मेल नहीं खाते",
"Set sync passphrase": "सिंक पासफ़्रेज़ सेट करें",
"Enter sync passphrase": "सिंक पासफ़्रेज़ दर्ज करें",
"A sync passphrase encrypts your sensitive fields (like OPDS catalog credentials) before they sync. We never see this passphrase. Pick something memorable — there is no recovery without it.": "एक सिंक पासफ़्रेज़ आपके संवेदनशील फ़ील्ड (जैसे OPDS कैटलॉग क्रेडेंशियल) को सिंक होने से पहले एन्क्रिप्ट करता है। हम यह पासफ़्रेज़ कभी नहीं देखते। ऐसा कुछ चुनें जो याद रहे — इसके बिना कोई पुनर्प्राप्ति नहीं है।",
"Enter the sync passphrase you set on another device to decrypt your synced credentials.": "अपने सिंक किए गए क्रेडेंशियल को डिक्रिप्ट करने के लिए वह सिंक पासफ़्रेज़ दर्ज करें जो आपने किसी अन्य डिवाइस पर सेट किया था।",
"Confirm passphrase": "पासफ़्रेज़ की पुष्टि करें",
"Unlock": "अनलॉक करें",
"Set PIN": "PIN सेट करें",
"Change PIN": "PIN बदलें",
"Disable PIN": "PIN अक्षम करें",
"Enter your current PIN, then choose a new 4-digit PIN.": "अपना वर्तमान PIN दर्ज करें, फिर एक नया 4-अंकों का PIN चुनें।",
"Enter your current PIN to disable the app lock.": "ऐप लॉक अक्षम करने के लिए अपना वर्तमान PIN दर्ज करें।",
"PIN must be {{length}} digits": "PIN {{length}} अंकों का होना चाहिए",
"PINs do not match": "PIN मेल नहीं खाते",
"Current PIN": "वर्तमान PIN",
"New PIN": "नया PIN",
"Confirm new PIN": "नए PIN की पुष्टि करें",
"Manage Sync": "सिंक प्रबंधित करें",
"Imported book files and library metadata": "आयातित पुस्तक फ़ाइलें और लाइब्रेरी मेटाडेटा",
"Last-read position, bookmarks, and per-book preferences": "अंतिम पठन स्थिति, बुकमार्क और प्रति-पुस्तक प्राथमिकताएँ",
"Highlights and notes": "हाइलाइट और नोट्स",
"Fonts": "फ़ॉन्ट",
"Custom font files": "कस्टम फ़ॉन्ट फ़ाइलें",
"Backgrounds": "पृष्ठभूमियाँ",
"Custom background textures": "कस्टम पृष्ठभूमि बनावट",
"OPDS catalogs": "OPDS कैटलॉग",
"Saved catalog URLs and (encrypted) credentials": "सहेजे गए कैटलॉग URL और (एन्क्रिप्टेड) क्रेडेंशियल्स",
"Wrong sync passphrase — synced credentials could not be decrypted": "गलत सिंक पासफ़्रेज़ — सिंक की गई क्रेडेंशियल्स को डिक्रिप्ट नहीं किया जा सका",
"Sync passphrase data on the server was reset. Re-encrypting your credentials under the new passphrase…": "सर्वर पर सिंक पासफ़्रेज़ डेटा रीसेट कर दिया गया। आपकी क्रेडेंशियल्स को नए पासफ़्रेज़ से पुनः एन्क्रिप्ट किया जा रहा है…",
"Failed to decrypt synced credentials": "सिंक की गई क्रेडेंशियल्स को डिक्रिप्ट करने में विफल",
"Imported dictionary bundles and settings": "आयातित शब्दकोश बंडल और सेटिंग्स",
"Choose what syncs across your devices. Disabling a category stops this device from sending or receiving rows of that kind. Anything already on the server is left alone, re-enabling resumes from where you stopped.": "चुनें कि आपके डिवाइसों के बीच क्या सिंक हो। किसी श्रेणी को अक्षम करने पर यह डिवाइस उस प्रकार की प्रविष्टियाँ भेजना या प्राप्त करना बंद कर देता है। सर्वर पर पहले से मौजूद कुछ भी नहीं बदलता, फिर से सक्षम करने पर वहीं से जारी होता है जहाँ आपने रोका था।",
"Data Sync": "डेटा सिंक",
"Manage Fonts": "फ़ॉन्ट प्रबंधित करें",
"App settings": "ऐप सेटिंग्स",
"Theme, highlight colours, integrations (KOSync, Readwise, Hardcover), and dictionary order": "थीम, हाइलाइट रंग, एकीकरण (KOSync, Readwise, Hardcover) और शब्दकोश क्रम",
"Required while Dictionaries sync is enabled": "शब्दकोश सिंक सक्षम होने पर आवश्यक",
"Resets in {{hours}} hr {{minutes}} min": "{{hours}} घं {{minutes}} मि में रीसेट",
"Pick a 4-digit PIN. You will need to enter it every time you open Readest. There is no way to recover a forgotten PIN. Clearing the app data is the only way to reset it.": "एक 4-अंकों का पिन चुनें। Readest खोलने पर आपको हर बार इसे दर्ज करना होगा। भूले हुए पिन को पुनः प्राप्त करने का कोई तरीका नहीं है। ऐप डेटा मिटाना इसे रीसेट करने का एकमात्र तरीका है।",
"Credentials": "क्रेडेंशियल",
"Tokens, usernames, and passwords for OPDS, KOReader, Hardcover, and Readwise. When disabled, credentials remain on this device only and are never uploaded.": "OPDS, KOReader, Hardcover और Readwise के लिए टोकन, उपयोगकर्ता नाम और पासवर्ड। अक्षम होने पर, क्रेडेंशियल केवल इसी डिवाइस पर रहते हैं और कभी अपलोड नहीं किए जाते।",
"Sensitive synced fields are encrypted before upload. Set a passphrase now or later when encryption is needed.": "संवेदनशील सिंक किए गए फ़ील्ड अपलोड से पहले एन्क्रिप्ट किए जाते हैं। अभी पासफ़्रेज़ सेट करें या बाद में जब एन्क्रिप्शन की आवश्यकता हो।",
"Saved to this account. You will be prompted for it when decrypting credentials.": "इस खाते में सहेजा गया। क्रेडेंशियल डिक्रिप्ट करते समय आपसे पूछा जाएगा।",
"Reading progress on this device differs from \"{{deviceName}}\".": "इस डिवाइस पर पढ़ने की प्रगति \"{{deviceName}}\" से भिन्न है।",
"This device": "यह डिवाइस",
"Red": "लाल",
"Yellow": "पीला",
"Green": "हरा",
"Blue": "नीला",
"Violet": "बैंगनी",
"Edit color": "रंग संपादित करें",
"Add custom color": "कस्टम रंग जोड़ें",
"Add label": "लेबल जोड़ें",
"Choose color": "रंग चुनें",
"Connected to Hardcover. Last synced {{time}}.": "Hardcover से कनेक्ट किया गया। अंतिम सिंक {{time}}।",
"Integrations": "एकीकरण",
"Hardcover": "Hardcover",
"Connected to Readwise. Last synced {{time}}.": "Readwise से कनेक्ट किया गया। अंतिम सिंक {{time}}।",
"Readwise": "Readwise",
"Browse and download books from online catalogs.": "ऑनलाइन कैटलॉग से पुस्तकें ब्राउज़ करें और डाउनलोड करें।",
"Connected as {{user}}": "{{user}} के रूप में कनेक्ट किया गया",
"Not connected": "कनेक्ट नहीं है",
"{{count}} catalog_one": "{{count}} कैटलॉग",
"{{count}} catalog_other": "{{count}} कैटलॉग",
"No catalogs": "कोई कैटलॉग नहीं",
"Connect Readest to external services for sync, highlights, and catalogs.": "सिंक, हाइलाइट और कैटलॉग के लिए Readest को बाहरी सेवाओं से कनेक्ट करें।",
"Reading Sync": "रीडिंग सिंक",
"Content Sources": "सामग्री स्रोत",
"Scroll tabs": "टैब स्क्रॉल करें",
"Catalog actions": "कैटलॉग क्रियाएँ",
"File Content": "फ़ाइल सामग्री",
"Start your library": "अपनी लाइब्रेरी शुरू करें",
"Pick a book from your device to add it to your library.": "अपनी लाइब्रेरी में जोड़ने के लिए अपने डिवाइस से एक किताब चुनें।",
"Drop a book anywhere on this window, or pick one from your computer.": "इस विंडो में कहीं भी एक किताब छोड़ें, या अपने कंप्यूटर से कोई एक चुनें।",
"Sign in to sync your library": "अपनी लाइब्रेरी सिंक करने के लिए साइन इन करें"
}
File diff suppressed because it is too large Load Diff
@@ -102,11 +102,9 @@
"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.",
"Wikipedia": "Wikipedia",
"Writing Mode": "Mode Menulis",
"Your Library": "Perpustakaan Anda",
"TTS not supported for PDF": "TTS tidak didukung untuk PDF",
"Override Book Font": "Ganti Font Buku",
"Apply to All Books": "Terapkan ke semua buku",
"Apply to This Book": "Terapkan ke buku ini",
@@ -173,9 +171,6 @@
"Token": "Token",
"Your OTP token": "Token OTP Anda",
"Verify token": "Verifikasi token",
"Sign in with Google": "Masuk dengan Google",
"Sign in with Apple": "Masuk dengan Apple",
"Sign in with GitHub": "Masuk dengan GitHub",
"Account": "Akun",
"Failed to delete user. Please try again later.": "Gagal menghapus pengguna. Silakan coba lagi nanti.",
"Community Support": "Dukungan Komunitas",
@@ -188,7 +183,6 @@
"RTL Direction": "Arah RTL",
"Maximum Column Height": "Tinggi Kolom Maksimum",
"Maximum Column Width": "Lebar Kolom Maksimum",
"Continuous Scroll": "Gulir Terus",
"Fullscreen": "Layar Penuh",
"No supported files found. Supported formats: {{formats}}": "Tidak ada file yang didukung ditemukan. Format yang didukung: {{formats}}",
"Drop to Import Books": "Jatuhkan untuk Mengimpor Buku",
@@ -302,7 +296,6 @@
"No notes match your search": "Tidak ada catatan ditemukan",
"Search notes and excerpts...": "Cari catatan...",
"Sign in to Sync": "Masuk untuk Sinkronisasi",
"Synced at {{time}}": "Disinkronkan pada {{time}}",
"Never synced": "Belum pernah disinkronkan",
"Show Remaining Time": "Perlihatkan Waktu Tersisa",
"{{time}} min left in chapter": "{{time}} menit tersisa di bab",
@@ -335,10 +328,10 @@
"Fit": "Pas",
"Reset {{settings}}": "Atur Ulang {{settings}}",
"Reset Settings": "Atur Ulang Pengaturan",
"{{count}} pages left in chapter_other": "{{count}} halaman tersisa di bab ini",
"{{count}} pages left in chapter_other": "<0>{{count}}</0><1> halaman tersisa di bab ini</1>",
"Show Remaining Pages": "Tampilkan halaman tersisa",
"Source Han Serif CN VF": "Source Han Serif",
"Huiwen-mincho": "Huiwen Mincho",
"Source Han Serif CN": "Source Han Serif",
"Huiwen-MinchoGBK": "Huiwen Mincho",
"KingHwa_OldSong": "KingHwa Song",
"Manage Subscription": "Kelola Langganan",
"Coming Soon": "Segera Hadir",
@@ -462,14 +455,12 @@
"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",
"Checksum Method": "Metode Checksum",
"File Content (recommended)": "Konten File (disarankan)",
"File Name": "Nama File",
"Device Name": "Nama Perangkat",
"Connect to your KOReader Sync server.": "Hubungkan ke server Sinkronisasi KOReader Anda.",
@@ -480,10 +471,7 @@
"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}}%)",
@@ -598,11 +586,10 @@
"Size": "Ukuran",
"Cover": "Sampul",
"Contain": "Mengandung",
"{{number}} pages left in chapter": "{{number}} halaman tersisa di bab",
"{{number}} pages left in chapter": "<0>{{number}}</0><1> halaman tersisa di bab</1>",
"Device": "Perangkat",
"E-Ink Mode": "Mode E-Ink",
"Highlight Colors": "Warna Sorotan",
"Auto Screen Brightness": "Auto Kecerahan Layar",
"Pagination": "Paginasi",
"Disable Double Tap": "Nonaktifkan Ketuk Ganda",
"Tap to Paginate": "Ketuk untuk Paginasi",
@@ -637,7 +624,6 @@
"Strikethrough": "Garis Tengah",
"Squiggly": "Bergelombang",
"Outline": "Garis Besar",
"Save Current Color": "Simpan Warna Saat Ini",
"Quick Colors": "Warna Cepat",
"Highlighter": "Penyorot",
"Save Book Cover": "Simpan Sampul Buku",
@@ -695,9 +681,7 @@
"Validating...": "Memvalidasi...",
"View All": "Lihat Semua",
"Forward": "Maju",
"OPDS Catalog": "Katalog OPDS",
"Home": "Beranda",
"Library": "Perpustakaan",
"{{count}} items_other": "{{count}} item",
"Download completed": "Unduhan selesai",
"Download failed": "Unduhan gagal",
@@ -716,5 +700,692 @@
"Last": "Terakhir",
"Cannot Load Page": "Tidak dapat memuat halaman",
"An error occurred": "Terjadi kesalahan",
"Online Library": "Perpustakaan Online"
"Online Library": "Perpustakaan Online",
"URL must start with http:// or https://": "URL harus diawali dengan http:// atau https://",
"Title, Author, Tag, etc...": "Judul, Penulis, Tag, dll...",
"Query": "Kueri",
"Subject": "Subjek",
"Enter {{terms}}": "Masukkan {{terms}}",
"No search results found": "Tidak ada hasil pencarian ditemukan",
"Failed to load OPDS feed: {{status}} {{statusText}}": "Gagal memuat feed OPDS: {{status}} {{statusText}}",
"Search in {{title}}": "Cari di {{title}}",
"Manage Storage": "Kelola Penyimpanan",
"Failed to load files": "Gagal memuat file",
"Deleted {{count}} file(s)_other": "{{count}} file dihapus",
"Failed to delete {{count}} file(s)_other": "Gagal menghapus {{count}} file",
"Failed to delete files": "Gagal menghapus file",
"Total Files": "Total File",
"Total Size": "Total Ukuran",
"Quota": "Kuota",
"Used": "Digunakan",
"Files": "File",
"Search files...": "Cari file...",
"Newest First": "Terbaru",
"Oldest First": "Terlama",
"Largest First": "Terbesar",
"Smallest First": "Terkecil",
"Name A-Z": "Nama A-Z",
"Name Z-A": "Nama Z-A",
"{{count}} selected_other": "{{count}} dipilih",
"Delete Selected": "Hapus yang Dipilih",
"Created": "Dibuat",
"No files found": "Tidak ada file ditemukan",
"No files uploaded yet": "Belum ada file diunggah",
"files": "file",
"Page {{current}} of {{total}}": "Halaman {{current}} dari {{total}}",
"Are you sure to delete {{count}} selected file(s)?_other": "Yakin ingin menghapus {{count}} file yang dipilih?",
"Cloud Storage Usage": "Penggunaan Penyimpanan Cloud",
"Rename Group": "Ganti Nama Grup",
"From Directory": "Dari Direktori",
"Successfully imported {{count}} book(s)_other": "Berhasil mengimpor {{count}} buku",
"Count": "Jumlah",
"Start Page": "Halaman Awal",
"Search in OPDS Catalog...": "Cari di Katalog OPDS...",
"Please log in to use advanced TTS features": "Silakan masuk untuk menggunakan fitur TTS lanjutan",
"Word limit of 30 words exceeded.": "Batas 30 kata telah terlampaui.",
"Proofread": "Koreksi",
"Current selection": "Pilihan saat ini",
"All occurrences in this book": "Semua kemunculan di buku ini",
"All occurrences in your library": "Semua kemunculan di perpustakaan Anda",
"Selected text:": "Teks terpilih:",
"Replace with:": "Ganti dengan:",
"Enter text...": "Masukkan teks…",
"Case sensitive:": "Peka huruf besar dan kecil:",
"Scope:": "Cakupan:",
"Selection": "Pilihan",
"Library": "Perpustakaan",
"Yes": "Ya",
"No": "Tidak",
"Proofread Replacement Rules": "Aturan penggantian koreksi",
"Selected Text Rules": "Aturan teks terpilih",
"No selected text replacement rules": "Tidak ada aturan penggantian untuk teks terpilih",
"Book Specific Rules": "Aturan khusus buku",
"No book-level replacement rules": "Tidak ada aturan penggantian tingkat buku",
"Disable Quick Action": "Nonaktifkan Aksi Cepat",
"Enable Quick Action on Selection": "Aktifkan Aksi Cepat saat memilih",
"None": "Tidak ada",
"Annotation Tools": "Alat Anotasi",
"Enable Quick Actions": "Aktifkan Aksi Cepat",
"Quick Action": "Aksi Cepat",
"Copy to Notebook": "Salin ke Buku Catatan",
"Copy text after selection": "Salin teks setelah pemilihan",
"Highlight text after selection": "Sorot teks setelah pemilihan",
"Annotate text after selection": "Anotasi teks setelah pemilihan",
"Search text after selection": "Cari teks setelah pemilihan",
"Look up text in dictionary after selection": "Cari teks di kamus setelah pemilihan",
"Translate text after selection": "Terjemahkan teks setelah pemilihan",
"Read text aloud after selection": "Bacakan teks setelah pemilihan",
"Proofread text after selection": "Periksa teks setelah pemilihan",
"{{activeCount}} active, {{pendingCount}} pending": "{{activeCount}} aktif, {{pendingCount}} menunggu",
"{{failedCount}} failed": "{{failedCount}} gagal",
"Waiting...": "Menunggu...",
"Failed": "Gagal",
"Completed": "Selesai",
"Cancelled": "Dibatalkan",
"Retry": "Coba lagi",
"Active": "Aktif",
"Transfer Queue": "Antrian Transfer",
"Upload All": "Unggah Semua",
"Download All": "Unduh Semua",
"Resume Transfers": "Lanjutkan Transfer",
"Pause Transfers": "Jeda Transfer",
"Pending": "Menunggu",
"No transfers": "Tidak ada transfer",
"Retry All": "Coba Semua Lagi",
"Clear Completed": "Hapus Selesai",
"Clear Failed": "Hapus Gagal",
"Upload queued: {{title}}": "Unggahan dalam antrian: {{title}}",
"Download queued: {{title}}": "Unduhan dalam antrian: {{title}}",
"Book not found in library": "Buku tidak ditemukan di perpustakaan",
"Unknown error": "Kesalahan tidak diketahui",
"Please log in to continue": "Silakan masuk untuk melanjutkan",
"Cloud File Transfers": "Transfer File Cloud",
"Show Search Results": "Tampilkan hasil pencarian",
"Search results for '{{term}}'": "Hasil untuk '{{term}}'",
"Close Search": "Tutup pencarian",
"Previous Result": "Hasil sebelumnya",
"Next Result": "Hasil berikutnya",
"Bookmarks": "Penanda",
"Annotations": "Anotasi",
"Show Results": "Tampilkan Hasil",
"Clear search": "Hapus pencarian",
"Clear search history": "Hapus riwayat pencarian",
"Tap to Toggle Footer": "Ketuk untuk beralih footer",
"Show Current Time": "Tampilkan Waktu Sekarang",
"Use 24 Hour Clock": "Gunakan Format 24 Jam",
"Show Current Battery Status": "Tampilkan Status Baterai Saat Ini",
"Exported successfully": "Berhasil diekspor",
"Book exported successfully.": "Buku berhasil diekspor.",
"Failed to export the book.": "Gagal mengekspor buku.",
"Export Book": "Ekspor Buku",
"Whole word:": "Kata utuh:",
"Date Published": "Tanggal terbit",
"Only for TTS:": "Hanya untuk TTS:",
"Uploaded": "Diunggah",
"Downloaded": "Diunduh",
"Deleted": "Dihapus",
"Note:": "Catatan:",
"Time:": "Waktu:",
"Format Options": "Opsi Format",
"Export Date": "Tanggal Ekspor",
"Chapter Titles": "Judul Bab",
"Chapter Separator": "Pemisah Bab",
"Highlights": "Sorotan",
"Note Date": "Tanggal Catatan",
"Advanced": "Lanjutan",
"Hide": "Sembunyikan",
"Show": "Tampilkan",
"Use Custom Template": "Gunakan Template Kustom",
"Export Template": "Template Ekspor",
"Template Syntax:": "Sintaks Template:",
"Insert value": "Masukkan nilai",
"Format date (locale)": "Format tanggal (lokal)",
"Format date (custom)": "Format tanggal (kustom)",
"Conditional": "Kondisional",
"Loop": "Loop",
"Available Variables:": "Variabel yang Tersedia:",
"Book title": "Judul buku",
"Book author": "Penulis buku",
"Export date": "Tanggal ekspor",
"Array of chapters": "Daftar bab",
"Chapter title": "Judul bab",
"Array of annotations": "Daftar anotasi",
"Highlighted text": "Teks yang disorot",
"Annotation note": "Catatan anotasi",
"Date Format Tokens:": "Token Format Tanggal:",
"Year (4 digits)": "Tahun (4 digit)",
"Month (01-12)": "Bulan (01-12)",
"Day (01-31)": "Hari (01-31)",
"Hour (00-23)": "Jam (00-23)",
"Minute (00-59)": "Menit (00-59)",
"Second (00-59)": "Detik (00-59)",
"Show Source": "Tampilkan Sumber",
"No content to preview": "Tidak ada konten untuk dipratinjau",
"Export": "Ekspor",
"Set Timeout": "Atur batas waktu",
"Select Voice": "Pilih suara",
"Toggle Sticky Bottom TTS Bar": "Alihkan bilah TTS tetap",
"Display what I'm reading on Discord": "Tampilkan buku yang sedang dibaca di Discord",
"Show on Discord": "Tampilkan di Discord",
"Instant {{action}}": "{{action}} Instan",
"Instant {{action}} Disabled": "{{action}} Instan Dinonaktifkan",
"Annotation": "Anotasi",
"Reset Template": "Atur Ulang Template",
"Annotation style": "Gaya anotasi",
"Annotation color": "Warna anotasi",
"Annotation time": "Waktu anotasi",
"AI": "AI",
"Are you sure you want to re-index this book?": "Apakah Anda yakin ingin mengindeks ulang buku ini?",
"Enable AI in Settings": "Aktifkan AI di Pengaturan",
"Index This Book": "Indeks Buku Ini",
"Enable AI search and chat for this book": "Aktifkan pencarian AI dan obrolan untuk buku ini",
"Start Indexing": "Mulai Mengindeks",
"Indexing book...": "Mengindeks buku...",
"Preparing...": "Mempersiapkan...",
"Delete this conversation?": "Hapus percakapan ini?",
"No conversations yet": "Belum ada percakapan",
"Start a new chat to ask questions about this book": "Mulai obrolan baru untuk mengajukan pertanyaan tentang buku ini",
"Rename": "Ubah nama",
"New Chat": "Obrolan Baru",
"Chat": "Obrolan",
"Please enter a model ID": "Silakan masukkan ID model",
"Model not available or invalid": "Model tidak tersedia atau tidak valid",
"Failed to validate model": "Gagal memvalidasi model",
"Couldn't connect to Ollama. Is it running?": "Tidak dapat terhubung ke Ollama. Apakah sedang berjalan?",
"Invalid API key or connection failed": "Kunci API tidak valid atau koneksi gagal",
"Connection failed": "Koneksi gagal",
"AI Assistant": "Asisten AI",
"Enable AI Assistant": "Aktifkan Asisten AI",
"Provider": "Penyedia",
"Ollama (Local)": "Ollama (Lokal)",
"AI Gateway (Cloud)": "Gateway AI (Cloud)",
"Ollama Configuration": "Konfigurasi Ollama",
"Refresh Models": "Segarkan Model",
"AI Model": "Model AI",
"No models detected": "Tidak ada model terdeteksi",
"AI Gateway Configuration": "Konfigurasi Gateway AI",
"Choose from a selection of high-quality, economical AI models. You can also bring your own model by selecting \"Custom Model\" below.": "Pilih dari pilihan model AI berkualitas tinggi dan ekonomis. Anda juga dapat menggunakan model Anda sendiri dengan memilih \"Model Kustom\" di bawah.",
"API Key": "Kunci API",
"Get Key": "Dapatkan Kunci",
"Model": "Model",
"Custom Model...": "Model Kustom...",
"Custom Model ID": "ID Model Kustom",
"Validate": "Validasi",
"Model available": "Model tersedia",
"Connection": "Koneksi",
"Test Connection": "Uji Koneksi",
"Connected": "Terhubung",
"Custom Colors": "Warna Kustom",
"Color E-Ink Mode": "Mode E-Ink Berwarna",
"Reading Ruler": "Penggaris Membaca",
"Enable Reading Ruler": "Aktifkan Penggaris Membaca",
"Lines to Highlight": "Baris untuk Disorot",
"Ruler Color": "Warna Penggaris",
"Command Palette": "Palet Perintah",
"Search settings and actions...": "Cari pengaturan dan tindakan...",
"No results found for": "Tidak ada hasil ditemukan untuk",
"Type to search settings and actions": "Ketik untuk mencari pengaturan dan tindakan",
"Recent": "Terbaru",
"navigate": "navigasi",
"select": "pilih",
"close": "tutup",
"Search Settings": "Cari Pengaturan",
"Page Margins": "Margin Halaman",
"AI Provider": "Penyedia AI",
"Ollama URL": "URL Ollama",
"Ollama Model": "Model Ollama",
"AI Gateway Model": "Model AI Gateway",
"Actions": "Tindakan",
"Navigation": "Navigasi",
"Set status for {{count}} book(s)_other": "Atur status untuk {{count}} buku",
"Mark as Unread": "Tandai sebagai Belum Dibaca",
"Mark as Finished": "Tandai sebagai Selesai",
"Finished": "Selesai",
"Unread": "Belum Dibaca",
"Clear Status": "Hapus Status",
"Status": "Status",
"Loading": "Memuat...",
"Exit Paragraph Mode": "Keluar dari Mode Paragraf",
"Paragraph Mode": "Mode Paragraf",
"Embedding Model": "Model Penyematan",
"{{count}} book(s) synced_other": "{{count}} buku telah disinkronkan",
"Unable to start RSVP": "Tidak dapat memulai RSVP",
"RSVP not supported for PDF": "RSVP tidak didukung untuk PDF",
"Select Chapter": "Pilih Bab",
"Context": "Konteks",
"Ready": "Siap",
"Chapter Progress": "Kemajuan Bab",
"words": "kata",
"{{time}} left": "{{time}} tersisa",
"Reading progress": "Kemajuan membaca",
"Skip back 15 words": "Lompat mundur 15 kata",
"Back 15 words (Shift+Left)": "Mundur 15 kata (Shift+Kiri)",
"Pause (Space)": "Jeda (Spasi)",
"Play (Space)": "Putar (Spasi)",
"Skip forward 15 words": "Lompat maju 15 kata",
"Forward 15 words (Shift+Right)": "Maju 15 kata (Shift+Kanan)",
"Decrease speed": "Kurangi kecepatan",
"Slower (Left/Down)": "Lebih lambat (Kiri/Bawah)",
"Increase speed": "Tambah kecepatan",
"Faster (Right/Up)": "Lebih cepat (Kanan/Atas)",
"Start RSVP Reading": "Mulai Membaca RSVP",
"Choose where to start reading": "Pilih tempat untuk mulai membaca",
"From Chapter Start": "Dari Awal Bab",
"Start reading from the beginning of the chapter": "Mulai membaca dari awal bab",
"Resume": "Lanjutkan",
"Continue from where you left off": "Lanjutkan dari tempat Anda berhenti",
"From Current Page": "Dari Halaman Saat Ini",
"Start from where you are currently reading": "Mulai dari tempat Anda sedang membaca",
"From Selection": "Dari Pilihan",
"Speed Reading Mode": "Mode Baca Cepat",
"Scroll left": "Gulir ke kiri",
"Scroll right": "Gulir ke kanan",
"Library Sync Progress": "Kemajuan Sinkronisasi Pustaka",
"Back to library": "Kembali ke perpustakaan",
"Group by...": "Kelompokkan menurut...",
"Export as Plain Text": "Ekspor sebagai Teks Biasa",
"Export as Markdown": "Ekspor sebagai Markdown",
"Show Page Navigation Buttons": "Tombol navigasi",
"Page {{number}}": "Halaman {{number}}",
"highlight": "sorotan",
"underline": "garis bawah",
"squiggly": "bergelombang",
"red": "merah",
"violet": "ungu",
"blue": "biru",
"green": "hijau",
"yellow": "kuning",
"Select {{style}} style": "Pilih gaya {{style}}",
"Select {{color}} color": "Pilih warna {{color}}",
"Close Book": "Tutup Buku",
"Speed Reading": "Membaca Cepat",
"Close Speed Reading": "Tutup Membaca Cepat",
"Authors": "Penulis",
"Books": "Buku",
"Groups": "Grup",
"Back to TTS Location": "Kembali ke Lokasi TTS",
"Metadata": "Metadata",
"Image viewer": "Penampil gambar",
"Previous Image": "Gambar Sebelumnya",
"Next Image": "Gambar Berikutnya",
"Zoomed": "Diperbesar",
"Zoom level": "Tingkat zoom",
"Table viewer": "Penampil tabel",
"Unable to connect to Readwise. Please check your network connection.": "Tidak dapat terhubung ke Readwise. Silakan periksa koneksi jaringan Anda.",
"Invalid Readwise access token": "Token akses Readwise tidak valid",
"Disconnected from Readwise": "Terputus dari Readwise",
"Never": "Tidak pernah",
"Sync Enabled": "Sinkronisasi Diaktifkan",
"Disconnect": "Putuskan sambungan",
"Connect your Readwise account to sync highlights.": "Hubungkan akun Readwise Anda untuk menyinkronkan sorotan.",
"Get your access token at": "Dapatkan token akses Anda di",
"Access Token": "Token Akses",
"Paste your Readwise access token": "Tempelkan token akses Readwise Anda",
"Readwise Sync": "Sinkronisasi Readwise",
"Push Highlights": "Kirim Sorotan",
"Highlights synced to Readwise": "Sorotan disinkronkan ke Readwise",
"Readwise sync failed: no internet connection": "Sinkronisasi Readwise gagal: tidak ada koneksi internet",
"Readwise sync failed: {{error}}": "Sinkronisasi Readwise gagal: {{error}}",
"System Screen Brightness": "Kecerahan Layar Sistem",
"Page:": "Halaman:",
"Page: {{number}}": "Halaman: {{number}}",
"Annotation page number": "Nomor halaman anotasi",
"Translating...": "Menerjemahkan...",
"Show Battery Percentage": "Tampilkan Persentase Baterai",
"Hide Scrollbar": "Sembunyikan Bilah Gulir",
"Skip to last reading position": "Lompat ke posisi baca terakhir",
"Show context": "Tampilkan konteks",
"Hide context": "Sembunyikan konteks",
"Decrease font size": "Perkecil ukuran font",
"Increase font size": "Perbesar ukuran font",
"Focus": "Fokus",
"Theme color": "Warna tema",
"Import failed": "Impor gagal",
"Available Formatters:": "Formatter Tersedia:",
"Format date": "Format tanggal",
"Markdown block quote (> per line)": "Kutipan blok Markdown (> per baris)",
"Newlines to <br>": "Baris baru ke <br>",
"Change case": "Ubah huruf besar/kecil",
"Trim whitespace": "Pangkas spasi",
"Truncate to n characters": "Potong ke n karakter",
"Replace text": "Ganti teks",
"Fallback value": "Nilai cadangan",
"Get length": "Dapatkan panjang",
"First/last element": "Elemen pertama/terakhir",
"Join array": "Gabungkan array",
"Backup failed: {{error}}": "Pencadangan gagal: {{error}}",
"Select Backup": "Pilih Cadangan",
"Restore failed: {{error}}": "Pemulihan gagal: {{error}}",
"Backup & Restore": "Cadangkan & Pulihkan",
"Create a backup of your library or restore from a previous backup. Restoring will merge with your current library.": "Buat cadangan perpustakaan Anda atau pulihkan dari cadangan sebelumnya. Pemulihan akan digabungkan dengan perpustakaan Anda saat ini.",
"Backup Library": "Cadangkan Perpustakaan",
"Restore Library": "Pulihkan Perpustakaan",
"Creating backup...": "Membuat cadangan...",
"Restoring library...": "Memulihkan perpustakaan...",
"{{current}} of {{total}} items": "{{current}} dari {{total}} item",
"Backup completed successfully!": "Pencadangan berhasil!",
"Restore completed successfully!": "Pemulihan berhasil!",
"Your library has been saved to the selected location.": "Perpustakaan Anda telah disimpan di lokasi yang dipilih.",
"{{added}} books added, {{updated}} books updated.": "{{added}} buku ditambahkan, {{updated}} buku diperbarui.",
"Operation failed": "Operasi gagal",
"Loading library...": "Memuat perpustakaan...",
"{{count}} books refreshed_other": "{{count}} buku diperbarui",
"Failed to refresh metadata": "Gagal memperbarui metadata",
"Refresh Metadata": "Perbarui Metadata",
"Keyboard Shortcuts": "Pintasan Keyboard",
"View all keyboard shortcuts": "Lihat semua pintasan keyboard",
"Switch Sidebar Tab": "Ganti tab bilah sisi",
"Toggle Notebook": "Tampilkan/sembunyikan buku catatan",
"Search in Book": "Cari di buku",
"Toggle Scroll Mode": "Alihkan mode gulir",
"Toggle Select Mode": "Alihkan mode pilih",
"Toggle Bookmark": "Alihkan penanda",
"Toggle Text to Speech": "Alihkan teks ke suara",
"Play / Pause TTS": "Putar / Jeda TTS",
"Toggle Paragraph Mode": "Alihkan mode paragraf",
"Highlight Selection": "Sorot pilihan",
"Underline Selection": "Garis bawahi pilihan",
"Annotate Selection": "Beri anotasi pilihan",
"Search Selection": "Cari pilihan",
"Copy Selection": "Salin pilihan",
"Translate Selection": "Terjemahkan pilihan",
"Dictionary Lookup": "Cari di kamus",
"Read Aloud Selection": "Bacakan pilihan",
"Proofread Selection": "Periksa ejaan pilihan",
"Open Settings": "Buka pengaturan",
"Open Command Palette": "Buka palet perintah",
"Show Keyboard Shortcuts": "Tampilkan pintasan keyboard",
"Open Books": "Buka buku",
"Toggle Fullscreen": "Alihkan layar penuh",
"Close Window": "Tutup jendela",
"Quit App": "Keluar dari aplikasi",
"Go Left / Previous Page": "Ke kiri / Halaman sebelumnya",
"Go Right / Next Page": "Ke kanan / Halaman berikutnya",
"Go Up": "Ke atas",
"Go Down": "Ke bawah",
"Previous Chapter": "Bab sebelumnya",
"Next Chapter": "Bab berikutnya",
"Scroll Half Page Down": "Gulir setengah halaman ke bawah",
"Scroll Half Page Up": "Gulir setengah halaman ke atas",
"Save Note": "Simpan catatan",
"Single Section Scroll": "Gulir Satu Bagian",
"General": "Umum",
"Text to Speech": "Teks ke Suara",
"Zoom": "Zoom",
"Window": "Jendela",
"Your Bookshelf": "Rak Buku Anda",
"TTS": "Teks ke Suara",
"Media Info": "Info Media",
"Update Frequency": "Frekuensi Pembaruan",
"Every Sentence": "Setiap Kalimat",
"Every Paragraph": "Setiap Paragraf",
"Every Chapter": "Setiap Bab",
"TTS Media Info Update Frequency": "Frekuensi Pembaruan Info Media TTS",
"Select reading speed": "Pilih kecepatan membaca",
"Drag to seek": "Seret untuk mencari",
"Punctuation Delay": "Jeda tanda baca",
"Please confirm that this OPDS connection will be proxied through Readest servers on the web app before continuing.": "Harap konfirmasi bahwa koneksi OPDS ini akan diteruskan melalui server Readest di aplikasi web sebelum melanjutkan.",
"Custom Headers (optional)": "Header Kustom (opsional)",
"Add one header per line using \"Header-Name: value\".": "Tambahkan satu header per baris menggunakan \"Header-Name: value\".",
"I understand this OPDS connection will be proxied through Readest servers on the web app. If I do not trust Readest with these credentials or headers, I should use the native app instead.": "Saya memahami bahwa koneksi OPDS ini akan diteruskan melalui server Readest di aplikasi web. Jika saya tidak mempercayai Readest dengan kredensial atau header ini, saya harus menggunakan aplikasi native.",
"Unable to connect to Hardcover. Please check your network connection.": "Tidak dapat terhubung ke Hardcover. Periksa koneksi jaringan Anda.",
"Invalid Hardcover API token": "Token API Hardcover tidak valid",
"Disconnected from Hardcover": "Terputus dari Hardcover",
"Connect your Hardcover account to sync reading progress and notes.": "Hubungkan akun Hardcover Anda untuk menyinkronkan progres membaca dan catatan.",
"Get your API token from hardcover.app → Settings → API.": "Dapatkan token API Anda dari hardcover.app → Pengaturan → API.",
"API Token": "Token API",
"Paste your Hardcover API token": "Tempel token API Hardcover Anda",
"Hardcover Sync": "Sinkronisasi Hardcover",
"Push Notes": "Kirim Catatan",
"No annotations or excerpts to sync for this book.": "Tidak ada anotasi atau kutipan untuk disinkronkan pada buku ini.",
"Configure Hardcover in Settings first.": "Konfigurasikan Hardcover di Pengaturan terlebih dahulu.",
"No new Hardcover note changes to sync.": "Tidak ada perubahan catatan Hardcover baru untuk disinkronkan.",
"Hardcover synced: {{inserted}} new, {{updated}} updated, {{skipped}} unchanged": "Hardcover disinkronkan: {{inserted}} baru, {{updated}} diperbarui, {{skipped}} tidak berubah",
"Hardcover notes sync failed: {{error}}": "Sinkronisasi catatan Hardcover gagal: {{error}}",
"Reading progress synced to Hardcover": "Progres membaca disinkronkan ke Hardcover",
"Hardcover progress sync failed: {{error}}": "Sinkronisasi progres Hardcover gagal: {{error}}",
"ISBN": "ISBN",
"Keep existing source identifier": "Pertahankan pengidentifikasi sumber yang ada",
"Toggle Toolbar": "Alihkan Bilah Alat",
"Split Hyphens": "Pisahkan Tanda Hubung",
"Apply Theme Colors to PDF": "Terapkan Warna Tema ke PDF",
"Name": "Nama",
"Remove": "Hapus",
"Edit OPDS Catalog": "Edit Katalog OPDS",
"Save Changes": "Simpan Perubahan",
"Use Book Layout": "Gunakan tata letak buku",
"End of this section. Continue to the next.": "Akhir bagian ini. Lanjutkan ke bagian berikutnya.",
"Auto-download": "Unduh otomatis",
"Auto-download new items": "Unduh item baru otomatis",
"Automatically download new publications when the app syncs": "Otomatis unduh publikasi baru saat aplikasi disinkronkan",
"{{count}} new item(s) downloaded from OPDS_other": "{{count}} item baru telah diunduh dari OPDS",
"Failed to sync {{count}} OPDS catalog(s)_other": "Gagal menyinkronkan {{count}} katalog OPDS",
"Last synced {{when}}": "Sinkronisasi terakhir {{when}}",
"Failed downloads": "Unduhan gagal",
"No failed downloads": "Tidak ada unduhan gagal",
"Attempts: {{count}}_other": "Percobaan: {{count}}",
"Skip": "Lewati",
"Skip all": "Lewati semua",
"Retry all": "Coba lagi semua",
"{{count}} failed_other": "{{count}} gagal",
"(none)": "(tidak ada)",
"Identifiers": "Pengenal",
"Read (Stream)": "Baca (Stream)",
"Failed to start stream": "Gagal memulai stream",
"No dictionaries enabled": "Tidak ada kamus yang diaktifkan",
"Enable a dictionary in Settings → Language → Dictionaries.": "Aktifkan kamus di Pengaturan → Bahasa → Kamus.",
"Wiktionary": "Wiktionary",
"Drag to reorder": "Seret untuk mengurutkan ulang",
"Built-in": "Bawaan",
"Bundle is missing on this device. Re-import to use it.": "Paket tidak ada di perangkat ini. Impor ulang untuk menggunakannya.",
"This dictionary format is not supported.": "Format kamus ini tidak didukung.",
"MDict": "MDict",
"StarDict": "StarDict",
"Imported {{count}} dictionary_other": "{{count}} kamus diimpor",
"Skipped incomplete bundles: {{names}}": "Paket tidak lengkap dilewati: {{names}}",
"Failed to import dictionary: {{message}}": "Gagal mengimpor kamus: {{message}}",
"Dictionaries": "Kamus",
"Delete Dictionary": "Hapus Kamus",
"Importing…": "Mengimpor…",
"Import Dictionary": "Impor Kamus",
"No dictionaries available.": "Tidak ada kamus yang tersedia.",
"StarDict bundles need .ifo, .idx, and .dict.dz files (.syn optional).": "Paket StarDict memerlukan file .ifo, .idx, dan .dict.dz (.syn opsional).",
"Select all the bundle files together when importing.": "Pilih semua file paket sekaligus saat mengimpor.",
"Manage Dictionaries": "Kelola Kamus",
"Select Dictionary Files": "Pilih File Kamus",
"Read on Wikipedia →": "Baca di Wikipedia →",
"This link can't be opened": "Tautan ini tidak dapat dibuka",
"The annotation link is missing required information. The original link may have been truncated.": "Tautan anotasi tidak memiliki informasi yang diperlukan. Tautan asli mungkin telah terpotong.",
"Go to Readest": "Buka Readest",
"Open-source ebook reader for everyone, on every device.": "Pembaca ebook sumber terbuka untuk semua orang, di setiap perangkat.",
"Open in Readest": "Buka di Readest",
"If Readest didn't open automatically, choose an option below:": "Jika Readest tidak terbuka otomatis, pilih salah satu opsi di bawah:",
"Continue reading where you left off.": "Lanjutkan membaca dari tempat terakhir.",
"Readest logo": "Logo Readest",
"Opening Readest...": "Membuka Readest…",
"Open in Readest app": "Buka di aplikasi Readest",
"Continue in browser": "Lanjutkan di peramban",
"Don't have Readest?": "Belum punya Readest?",
"Book not in your library": "Buku tidak ada di pustaka Anda",
"Share Book": "Bagikan Buku",
"Could not create share link": "Tidak dapat membuat tautan berbagi",
"Link copied": "Tautan disalin",
"Could not copy link": "Tidak dapat menyalin tautan",
"Share revoked": "Berbagi dibatalkan",
"Could not revoke share": "Tidak dapat membatalkan berbagi",
"Uploading book…": "Mengunggah buku…",
"Generating…": "Membuat…",
"Generate share link": "Buat tautan berbagi",
"Share URL": "URL Berbagi",
"Copy link": "Salin tautan",
"Copied": "Disalin",
"Share via…": "Bagikan melalui…",
"Expires {{date}}": "Kedaluwarsa pada {{date}}",
"Revoking…": "Membatalkan…",
"Revoke share": "Batalkan berbagi",
"Sign in to share books": "Masuk untuk berbagi buku",
"Expiring soon": "Segera kedaluwarsa",
"Missing share token": "Token berbagi hilang",
"Could not add to your library": "Tidak dapat ditambahkan ke pustaka Anda",
"This share link is no longer available": "Tautan berbagi ini tidak lagi tersedia",
"The original link may have expired or been revoked.": "Tautan asli mungkin telah kedaluwarsa atau dibatalkan.",
"Get Readest": "Dapatkan Readest",
"Loading shared book…": "Memuat buku yang dibagikan…",
"Adding…": "Menambahkan…",
"Add to my library": "Tambahkan ke pustaka saya",
"Could not load your shares": "Tidak dapat memuat berbagi Anda",
"Revoked": "Dibatalkan",
"Expired": "Kedaluwarsa",
"Shared books": "Buku yang dibagikan",
"You haven't shared any books yet": "Anda belum membagikan buku apa pun",
"Open a book and tap Share to send it to a friend.": "Buka buku dan ketuk Bagikan untuk mengirimnya ke teman.",
"{{count}} active_other": "{{count}} aktif",
"{{count}} downloads_other": "{{count}} unduhan",
"starts at saved page": "mulai dari halaman tersimpan",
"More actions": "Tindakan lainnya",
"Loading…": "Memuat…",
"Load more": "Muat lainnya",
"Could not load shared book": "Tidak dapat memuat buku yang dibagikan",
"The share link is missing required information.": "Tautan berbagi tidak memiliki informasi yang diperlukan.",
"Please check your connection and try again.": "Periksa koneksi Anda dan coba lagi.",
"Sign in to import shared books": "Masuk untuk mengimpor buku yang dibagikan",
"Already in your library": "Sudah ada di pustaka Anda",
"Added to your library": "Ditambahkan ke pustaka Anda",
"Could not import shared book": "Tidak dapat mengimpor buku yang dibagikan",
"Manage Shared Links": "Kelola tautan berbagi",
"Expires in": "Kedaluwarsa dalam",
"{{count}} days_other": "{{count}} hari",
"Expires in {{count}} days_other": "Kedaluwarsa dalam {{count}} hari",
"Expires in {{count}} hours_other": "Kedaluwarsa dalam {{count}} jam",
"Open in app": "Buka di aplikasi",
"Shared with you": "Dibagikan dengan Anda",
"Downloading… {{percent}}%": "Mengunduh… {{percent}}%",
"Import progress": "Kemajuan impor",
"Share reading progress": "Bagikan progres baca",
"Includes your reading progress": "Termasuk progres baca Anda",
"Synced {{time}}": "Disinkronkan {{time}}",
"Sync Info": "Info sinkronisasi",
"Last Synced": "Sinkronisasi terakhir",
"DICT": "DICT",
"Slob": "Slob",
"DICT bundles need a .index file and a .dict.dz file.": "Bundel DICT memerlukan berkas .index dan berkas .dict.dz.",
"Slob bundles need a .slob file.": "Bundel Slob memerlukan berkas .slob.",
"URL template must start with http(s):// and contain %WORD%.": "Templat URL harus diawali http(s):// dan berisi %WORD%.",
"Web": "Web",
"Add Web Search": "Tambah Pencarian Web",
"Edit Web Search": "Edit Pencarian Web",
"e.g. Google": "mis. Google",
"URL Template": "Templat URL",
"Use %WORD% where the looked-up word should appear.": "Gunakan %WORD% di tempat kata yang dicari harus muncul.",
"Open the search result in your browser:": "Buka hasil pencarian di peramban Anda:",
"Open in {{name}}": "Buka di {{name}}",
"Book Fingerprint": "Sidik jari buku",
"Search the web": "Cari di web",
"App deeplink (readest://)": "Tautan dalam aplikasi (readest://)",
"Universal web link (https://)": "Tautan web universal (https://)",
"Name cannot be empty.": "Nama tidak boleh kosong.",
"Replaced {{count}} existing dictionary_other": "{{count}} kamus yang ada digantikan",
"Cancel Edit": "Batalkan Edit",
"Edit Dictionary": "Edit Kamus",
"MDict bundles use .mdx files; companion .mdd and .css files are optional.": "Paket MDict menggunakan berkas .mdx; berkas .mdd dan .css pendamping bersifat opsional.",
"Dictionary name": "Nama kamus",
"This audio can't play here — the dictionary uses an outdated format. Try one with Opus, MP3, or WAV audio.": "Audio ini tidak dapat diputar di sini — kamus menggunakan format usang. Coba kamus dengan audio Opus, MP3, atau WAV.",
"File uploaded: {{title}}": "File diunggah: {{title}}",
"File downloaded: {{title}}": "File diunduh: {{title}}",
"Deleted cloud copy of the file: {{title}}": "Salinan cloud file dihapus: {{title}}",
"Failed to upload file: {{title}}": "Gagal mengunggah file: {{title}}",
"Failed to download file: {{title}}": "Gagal mengunduh file: {{title}}",
"Failed to delete cloud copy of the file: {{title}}": "Gagal menghapus salinan cloud file: {{title}}",
"{{percentage}}% used": "{{percentage}}% terpakai",
"Set PIN…": "Atur PIN…",
"Require a 4-digit PIN to open Readest": "Wajibkan PIN 4 digit untuk membuka Readest",
"Change PIN…": "Ubah PIN…",
"Disable PIN…": "Nonaktifkan PIN…",
"Sync passphrase ready": "Frasa sandi sinkronisasi siap",
"This permanently deletes the encrypted credentials we sync (e.g., OPDS catalog passwords) on every device. Local copies are preserved. You will need to re-enter the sync passphrase or set a new one. Continue?": "Ini secara permanen menghapus kredensial terenkripsi yang kami sinkronkan (mis., kata sandi katalog OPDS) di setiap perangkat. Salinan lokal disimpan. Anda perlu memasukkan kembali frasa sandi sinkronisasi atau menyetel yang baru. Lanjutkan?",
"Sync passphrase forgotten — all encrypted fields cleared": "Frasa sandi sinkronisasi terlupakan — semua bidang terenkripsi dihapus",
"Sync passphrase": "Frasa sandi sinkronisasi",
"Set passphrase": "Setel frasa sandi",
"Forgot passphrase": "Lupa frasa sandi",
"Incorrect PIN": "PIN salah",
"App locked": "Aplikasi terkunci",
"Enter your PIN": "Masukkan PIN Anda",
"Readest is locked. Enter your 4-digit PIN to continue.": "Readest terkunci. Masukkan PIN 4 digit Anda untuk melanjutkan.",
"PIN code": "Kode PIN",
"Forgetting your PIN locks you out of this device. You'll need to clear the app's data to reset it.": "Lupa PIN akan mengunci Anda dari perangkat ini. Anda perlu menghapus data aplikasi untuk meresetnya.",
"Passphrase must be at least 8 characters": "Frasa sandi harus minimal 8 karakter",
"Passphrases do not match": "Frasa sandi tidak cocok",
"Set sync passphrase": "Setel frasa sandi sinkronisasi",
"Enter sync passphrase": "Masukkan frasa sandi sinkronisasi",
"A sync passphrase encrypts your sensitive fields (like OPDS catalog credentials) before they sync. We never see this passphrase. Pick something memorable — there is no recovery without it.": "Frasa sandi sinkronisasi mengenkripsi bidang sensitif Anda (seperti kredensial katalog OPDS) sebelum disinkronkan. Kami tidak pernah melihat frasa sandi ini. Pilih sesuatu yang mudah diingat — tidak ada pemulihan tanpanya.",
"Enter the sync passphrase you set on another device to decrypt your synced credentials.": "Masukkan frasa sandi sinkronisasi yang Anda setel di perangkat lain untuk mendekripsi kredensial yang tersinkron.",
"Confirm passphrase": "Konfirmasi frasa sandi",
"Unlock": "Buka kunci",
"Set PIN": "Atur PIN",
"Change PIN": "Ubah PIN",
"Disable PIN": "Nonaktifkan PIN",
"Enter your current PIN, then choose a new 4-digit PIN.": "Masukkan PIN Anda saat ini, lalu pilih PIN 4 digit baru.",
"Enter your current PIN to disable the app lock.": "Masukkan PIN Anda saat ini untuk menonaktifkan kunci aplikasi.",
"PIN must be {{length}} digits": "PIN harus terdiri dari {{length}} digit",
"PINs do not match": "PIN tidak cocok",
"Current PIN": "PIN saat ini",
"New PIN": "PIN baru",
"Confirm new PIN": "Konfirmasi PIN baru",
"Manage Sync": "Kelola sinkronisasi",
"Imported book files and library metadata": "Berkas buku yang diimpor dan metadata pustaka",
"Last-read position, bookmarks, and per-book preferences": "Posisi baca terakhir, penanda, dan preferensi per buku",
"Highlights and notes": "Sorotan dan catatan",
"Fonts": "Fon",
"Custom font files": "Berkas fon kustom",
"Backgrounds": "Latar belakang",
"Custom background textures": "Tekstur latar belakang kustom",
"OPDS catalogs": "Katalog OPDS",
"Saved catalog URLs and (encrypted) credentials": "URL katalog tersimpan dan kredensial (terenkripsi)",
"Wrong sync passphrase — synced credentials could not be decrypted": "Frasa sandi sinkronisasi salah — kredensial yang disinkronkan tidak dapat didekripsi",
"Sync passphrase data on the server was reset. Re-encrypting your credentials under the new passphrase…": "Data frasa sandi sinkronisasi di server telah direset. Mengenkripsi ulang kredensial Anda dengan frasa sandi baru…",
"Failed to decrypt synced credentials": "Gagal mendekripsi kredensial yang disinkronkan",
"Imported dictionary bundles and settings": "Bundel kamus yang diimpor dan pengaturan",
"Choose what syncs across your devices. Disabling a category stops this device from sending or receiving rows of that kind. Anything already on the server is left alone, re-enabling resumes from where you stopped.": "Pilih apa saja yang disinkronkan antar perangkatmu. Menonaktifkan sebuah kategori menghentikan perangkat ini mengirim atau menerima entri jenis itu. Apa pun yang sudah ada di server tetap utuh, mengaktifkannya kembali akan melanjutkan dari tempat kamu berhenti.",
"Data Sync": "Sinkronisasi Data",
"Manage Fonts": "Kelola Fon",
"App settings": "Pengaturan aplikasi",
"Theme, highlight colours, integrations (KOSync, Readwise, Hardcover), and dictionary order": "Tema, warna sorotan, integrasi (KOSync, Readwise, Hardcover), dan urutan kamus",
"Required while Dictionaries sync is enabled": "Diperlukan saat sinkronisasi Kamus diaktifkan",
"Resets in {{hours}} hr {{minutes}} min": "Direset dalam {{hours}} j {{minutes}} mnt",
"Pick a 4-digit PIN. You will need to enter it every time you open Readest. There is no way to recover a forgotten PIN. Clearing the app data is the only way to reset it.": "Pilih PIN 4 digit. Anda harus memasukkannya setiap kali membuka Readest. Tidak ada cara untuk memulihkan PIN yang terlupa. Menghapus data aplikasi adalah satu-satunya cara untuk mengaturnya ulang.",
"Credentials": "Kredensial",
"Tokens, usernames, and passwords for OPDS, KOReader, Hardcover, and Readwise. When disabled, credentials remain on this device only and are never uploaded.": "Token, nama pengguna, dan kata sandi untuk OPDS, KOReader, Hardcover, dan Readwise. Saat dinonaktifkan, kredensial hanya tetap di perangkat ini dan tidak pernah diunggah.",
"Sensitive synced fields are encrypted before upload. Set a passphrase now or later when encryption is needed.": "Bidang sinkron yang sensitif dienkripsi sebelum diunggah. Atur frasa sandi sekarang atau nanti saat enkripsi diperlukan.",
"Saved to this account. You will be prompted for it when decrypting credentials.": "Disimpan di akun ini. Anda akan diminta memasukkannya saat mendekripsi kredensial.",
"Reading progress on this device differs from \"{{deviceName}}\".": "Progres membaca di perangkat ini berbeda dari \"{{deviceName}}\".",
"This device": "Perangkat ini",
"Red": "Merah",
"Yellow": "Kuning",
"Green": "Hijau",
"Blue": "Biru",
"Violet": "Ungu",
"Edit color": "Edit warna",
"Add custom color": "Tambah warna kustom",
"Add label": "Tambah label",
"Choose color": "Pilih warna",
"Connected to Hardcover. Last synced {{time}}.": "Terhubung ke Hardcover. Sinkronisasi terakhir {{time}}.",
"Integrations": "Integrasi",
"Hardcover": "Hardcover",
"Connected to Readwise. Last synced {{time}}.": "Terhubung ke Readwise. Sinkronisasi terakhir {{time}}.",
"Readwise": "Readwise",
"Browse and download books from online catalogs.": "Telusuri dan unduh buku dari katalog online.",
"Connected as {{user}}": "Terhubung sebagai {{user}}",
"Not connected": "Tidak terhubung",
"{{count}} catalog_other": "{{count}} katalog",
"No catalogs": "Tidak ada katalog",
"Connect Readest to external services for sync, highlights, and catalogs.": "Hubungkan Readest ke layanan eksternal untuk sinkronisasi, sorotan, dan katalog.",
"Reading Sync": "Sinkronisasi Membaca",
"Content Sources": "Sumber Konten",
"Scroll tabs": "Gulir tab",
"Catalog actions": "Tindakan katalog",
"File Content": "Konten berkas",
"Start your library": "Mulai perpustakaan Anda",
"Pick a book from your device to add it to your library.": "Pilih buku dari perangkat Anda untuk ditambahkan ke perpustakaan Anda.",
"Drop a book anywhere on this window, or pick one from your computer.": "Jatuhkan buku di mana saja pada jendela ini, atau pilih satu dari komputer Anda.",
"Sign in to sync your library": "Masuk untuk menyinkronkan perpustakaan Anda"
}
@@ -102,11 +102,9 @@
"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.",
"Wikipedia": "Wikipedia",
"Writing Mode": "Modalità scrittura",
"Your Library": "La tua biblioteca",
"TTS not supported for PDF": "TTS non supportato per PDF",
"Override Book Font": "Sovrascrivi font libro",
"Apply to All Books": "Applica a tutti i libri",
"Apply to This Book": "Applica a questo libro",
@@ -173,9 +171,6 @@
"Token": "Token",
"Your OTP token": "Il tuo token OTP",
"Verify token": "Verifica token",
"Sign in with Google": "Accedi con Google",
"Sign in with Apple": "Accedi con Apple",
"Sign in with GitHub": "Accedi con GitHub",
"Account": "Account",
"Failed to delete user. Please try again later.": "Impossibile eliminare l'utente. Riprova più tardi.",
"Community Support": "Supporto della community",
@@ -188,7 +183,6 @@
"RTL Direction": "Direzione RTL",
"Maximum Column Height": "Altezza massima colonna",
"Maximum Column Width": "Larghezza massima colonna",
"Continuous Scroll": "Scorrimento continuo",
"Fullscreen": "Schermo intero",
"No supported files found. Supported formats: {{formats}}": "Nessun file supportato trovato. Formati supportati: {{formats}}",
"Drop to Import Books": "Rilascia per importare libri",
@@ -306,7 +300,6 @@
"No notes match your search": "Nessuna nota trovata",
"Search notes and excerpts...": "Cerca nelle note...",
"Sign in to Sync": "Accedi per sincronizzare",
"Synced at {{time}}": "Sincronizzato alle {{time}}",
"Never synced": "Mai sincronizzato",
"Show Remaining Time": "Mostra tempo rimanente",
"{{time}} min left in chapter": "{{time}} min rimasti nel capitolo",
@@ -341,12 +334,12 @@
"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",
"{{count}} pages left in chapter_one": "<0>{{count}}</0><1> pagina rimasta nel capitolo</1>",
"{{count}} pages left in chapter_many": "<0>{{count}}</0><1> pagine rimaste nel capitolo</1>",
"{{count}} pages left in chapter_other": "<0>{{count}}</0><1> pagine rimaste nel capitolo</1>",
"Show Remaining Pages": "Mostra pagine rimanenti",
"Source Han Serif CN VF": "Source Han Serif",
"Huiwen-mincho": "Huiwen Mincho",
"Source Han Serif CN": "Source Han Serif",
"Huiwen-MinchoGBK": "Huiwen Mincho",
"KingHwa_OldSong": "KingHwa Song",
"Manage Subscription": "Gestisci Abbonamento",
"Coming Soon": "Prossimamente",
@@ -470,14 +463,12 @@
"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",
"Checksum Method": "Metodo di Controllo",
"File Content (recommended)": "Contenuto del File (consigliato)",
"File Name": "Nome del File",
"Device Name": "Nome del Dispositivo",
"Connect to your KOReader Sync server.": "Connettiti al tuo server KOReader Sync.",
@@ -488,10 +479,7 @@
"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}}%)",
@@ -606,11 +594,10 @@
"Size": "Dimensione",
"Cover": "Copertura",
"Contain": "Contenere",
"{{number}} pages left in chapter": "{{number}} pagine rimaste nel capitolo",
"{{number}} pages left in chapter": "<0>{{number}}</0><1> pagine rimaste nel capitolo</1>",
"Device": "Dispositivo",
"E-Ink Mode": "Modalità E-Ink",
"Highlight Colors": "Colori Evidenziazione",
"Auto Screen Brightness": "Luminosità schermo automatica",
"Pagination": "Impaginazione",
"Disable Double Tap": "Disabilita Doppio Tap",
"Tap to Paginate": "Tocca per Impaginare",
@@ -645,7 +632,6 @@
"Strikethrough": "Barrato",
"Squiggly": "Ondulato",
"Outline": "Contorno",
"Save Current Color": "Salva Colore Corrente",
"Quick Colors": "Colori Veloci",
"Highlighter": "Evidenziatore",
"Save Book Cover": "Salva Copertina del Libro",
@@ -703,9 +689,7 @@
"Validating...": "Convalida in corso...",
"View All": "Vedi tutto",
"Forward": "Avanti",
"OPDS Catalog": "Catalogo OPDS",
"Home": "Home",
"Library": "Libreria",
"{{count}} items_one": "{{count}} elemento",
"{{count}} items_many": "{{count}} elementi",
"{{count}} items_other": "{{count}} elementi",
@@ -726,5 +710,732 @@
"Last": "Ultimo",
"Cannot Load Page": "Impossibile caricare la pagina",
"An error occurred": "Si è verificato un errore",
"Online Library": "Libreria online"
"Online Library": "Libreria online",
"URL must start with http:// or https://": "URL deve iniziare con http:// o https://",
"Title, Author, Tag, etc...": "Titolo, Autore, Tag, ecc...",
"Query": "Query",
"Subject": "Soggetto",
"Enter {{terms}}": "Inserisci {{terms}}",
"No search results found": "Nessun risultato di ricerca trovato",
"Failed to load OPDS feed: {{status}} {{statusText}}": "Impossibile caricare il feed OPDS: {{status}} {{statusText}}",
"Search in {{title}}": "Cerca in {{title}}",
"Manage Storage": "Gestisci archiviazione",
"Failed to load files": "Impossibile caricare i file",
"Deleted {{count}} file(s)_one": "È stato eliminato {{count}} file",
"Deleted {{count}} file(s)_many": "Sono stati eliminati {{count}} file",
"Deleted {{count}} file(s)_other": "Sono stati eliminati {{count}} file",
"Failed to delete {{count}} file(s)_one": "Impossibile eliminare {{count}} file",
"Failed to delete {{count}} file(s)_many": "Impossibile eliminare {{count}} file",
"Failed to delete {{count}} file(s)_other": "Impossibile eliminare {{count}} file",
"Failed to delete files": "Impossibile eliminare i file",
"Total Files": "File totali",
"Total Size": "Dimensione totale",
"Quota": "Quota",
"Used": "Utilizzato",
"Files": "File",
"Search files...": "Cerca file...",
"Newest First": "Più recenti",
"Oldest First": "Più vecchi",
"Largest First": "Più grandi",
"Smallest First": "Più piccoli",
"Name A-Z": "Nome A-Z",
"Name Z-A": "Nome Z-A",
"{{count}} selected_one": "{{count}} selezionato",
"{{count}} selected_many": "{{count}} selezionati",
"{{count}} selected_other": "{{count}} selezionati",
"Delete Selected": "Elimina selezionati",
"Created": "Creato",
"No files found": "Nessun file trovato",
"No files uploaded yet": "Nessun file caricato",
"files": "file",
"Page {{current}} of {{total}}": "Pagina {{current}} di {{total}}",
"Are you sure to delete {{count}} selected file(s)?_one": "Sei sicuro di voler eliminare {{count}} file selezionato?",
"Are you sure to delete {{count}} selected file(s)?_many": "Sei sicuro di voler eliminare {{count}} file selezionati?",
"Are you sure to delete {{count}} selected file(s)?_other": "Sei sicuro di voler eliminare {{count}} file selezionati?",
"Cloud Storage Usage": "Utilizzo archiviazione cloud",
"Rename Group": "Rinomina gruppo",
"From Directory": "Da directory",
"Successfully imported {{count}} book(s)_one": "Importato con successo 1 libro",
"Successfully imported {{count}} book(s)_many": "Importati con successo {{count}} libri",
"Successfully imported {{count}} book(s)_other": "Importati con successo {{count}} libri",
"Count": "Conteggio",
"Start Page": "Pagina iniziale",
"Search in OPDS Catalog...": "Cerca nel catalogo OPDS...",
"Please log in to use advanced TTS features": "Effettua il login per utilizzare le funzionalità TTS avanzate",
"Word limit of 30 words exceeded.": "È stato superato il limite di 30 parole.",
"Proofread": "Revisione",
"Current selection": "Selezione corrente",
"All occurrences in this book": "Tutte le occorrenze in questo libro",
"All occurrences in your library": "Tutte le occorrenze nella tua libreria",
"Selected text:": "Testo selezionato:",
"Replace with:": "Sostituisci con:",
"Enter text...": "Inserisci testo…",
"Case sensitive:": "Maiuscole/minuscole:",
"Scope:": "Ambito:",
"Selection": "Selezione",
"Library": "Libreria",
"Yes": "Sì",
"No": "No",
"Proofread Replacement Rules": "Regole di sostituzione per la revisione",
"Selected Text Rules": "Regole per il testo selezionato",
"No selected text replacement rules": "Nessuna regola di sostituzione per il testo selezionato",
"Book Specific Rules": "Regole specifiche del libro",
"No book-level replacement rules": "Nessuna regola di sostituzione a livello di libro",
"Disable Quick Action": "Disattiva azione rapida",
"Enable Quick Action on Selection": "Attiva azione rapida sulla selezione",
"None": "Nessuno",
"Annotation Tools": "Strumenti di annotazione",
"Enable Quick Actions": "Attiva azioni rapide",
"Quick Action": "Azione rapida",
"Copy to Notebook": "Copia nel taccuino",
"Copy text after selection": "Copia testo dopo la selezione",
"Highlight text after selection": "Evidenzia testo dopo la selezione",
"Annotate text after selection": "Annota testo dopo la selezione",
"Search text after selection": "Cerca testo dopo la selezione",
"Look up text in dictionary after selection": "Cerca testo nel dizionario dopo la selezione",
"Translate text after selection": "Traduci testo dopo la selezione",
"Read text aloud after selection": "Leggi ad alta voce il testo dopo la selezione",
"Proofread text after selection": "Correggi il testo dopo la selezione",
"{{activeCount}} active, {{pendingCount}} pending": "{{activeCount}} attivi, {{pendingCount}} in attesa",
"{{failedCount}} failed": "{{failedCount}} falliti",
"Waiting...": "In attesa...",
"Failed": "Fallito",
"Completed": "Completato",
"Cancelled": "Annullato",
"Retry": "Riprova",
"Active": "Attivi",
"Transfer Queue": "Coda trasferimenti",
"Upload All": "Carica tutti",
"Download All": "Scarica tutti",
"Resume Transfers": "Riprendi trasferimenti",
"Pause Transfers": "Sospendi trasferimenti",
"Pending": "In attesa",
"No transfers": "Nessun trasferimento",
"Retry All": "Riprova tutti",
"Clear Completed": "Cancella completati",
"Clear Failed": "Cancella falliti",
"Upload queued: {{title}}": "Caricamento in coda: {{title}}",
"Download queued: {{title}}": "Download in coda: {{title}}",
"Book not found in library": "Libro non trovato nella libreria",
"Unknown error": "Errore sconosciuto",
"Please log in to continue": "Accedi per continuare",
"Cloud File Transfers": "Trasferimenti file cloud",
"Show Search Results": "Mostra risultati",
"Search results for '{{term}}'": "Risultati per '{{term}}'",
"Close Search": "Chiudi ricerca",
"Previous Result": "Risultato precedente",
"Next Result": "Risultato successivo",
"Bookmarks": "Segnalibri",
"Annotations": "Annotazioni",
"Show Results": "Mostra risultati",
"Clear search": "Cancella ricerca",
"Clear search history": "Cancella cronologia ricerche",
"Tap to Toggle Footer": "Tocca per mostrare/nascondere il piè di pagina",
"Show Current Time": "Mostra ora attuale",
"Use 24 Hour Clock": "Usa formato 24 ore",
"Show Current Battery Status": "Mostra lo stato attuale della batteria",
"Exported successfully": "Esportato con successo",
"Book exported successfully.": "Libro esportato con successo.",
"Failed to export the book.": "Impossibile esportare il libro.",
"Export Book": "Esporta libro",
"Whole word:": "Parola intera:",
"Date Published": "Data di pubblicazione",
"Only for TTS:": "Solo per TTS:",
"Uploaded": "Caricato",
"Downloaded": "Scaricato",
"Deleted": "Eliminato",
"Note:": "Nota:",
"Time:": "Ora:",
"Format Options": "Opzioni formato",
"Export Date": "Data esportazione",
"Chapter Titles": "Titoli capitoli",
"Chapter Separator": "Separatore capitoli",
"Highlights": "Evidenziazioni",
"Note Date": "Data nota",
"Advanced": "Avanzate",
"Hide": "Nascondi",
"Show": "Mostra",
"Use Custom Template": "Usa modello personalizzato",
"Export Template": "Modello esportazione",
"Template Syntax:": "Sintassi modello:",
"Insert value": "Inserisci valore",
"Format date (locale)": "Formatta data (locale)",
"Format date (custom)": "Formatta data (personalizzato)",
"Conditional": "Condizionale",
"Loop": "Ciclo",
"Available Variables:": "Variabili disponibili:",
"Book title": "Titolo libro",
"Book author": "Autore libro",
"Export date": "Data esportazione",
"Array of chapters": "Elenco capitoli",
"Chapter title": "Titolo capitolo",
"Array of annotations": "Elenco annotazioni",
"Highlighted text": "Testo evidenziato",
"Annotation note": "Nota annotazione",
"Date Format Tokens:": "Token formato data:",
"Year (4 digits)": "Anno (4 cifre)",
"Month (01-12)": "Mese (01-12)",
"Day (01-31)": "Giorno (01-31)",
"Hour (00-23)": "Ora (00-23)",
"Minute (00-59)": "Minuto (00-59)",
"Second (00-59)": "Secondo (00-59)",
"Show Source": "Mostra sorgente",
"No content to preview": "Nessun contenuto da visualizzare",
"Export": "Esporta",
"Set Timeout": "Imposta timeout",
"Select Voice": "Seleziona voce",
"Toggle Sticky Bottom TTS Bar": "Attiva/disattiva barra TTS fissa",
"Display what I'm reading on Discord": "Mostra cosa sto leggendo su Discord",
"Show on Discord": "Mostra su Discord",
"Instant {{action}}": "{{action}} istantaneo",
"Instant {{action}} Disabled": "{{action}} istantaneo disattivato",
"Annotation": "Annotazione",
"Reset Template": "Reimposta modello",
"Annotation style": "Stile annotazione",
"Annotation color": "Colore annotazione",
"Annotation time": "Ora annotazione",
"AI": "IA",
"Are you sure you want to re-index this book?": "Sei sicuro di voler reindicizzare questo libro?",
"Enable AI in Settings": "Abilita IA nelle impostazioni",
"Index This Book": "Indicizza questo libro",
"Enable AI search and chat for this book": "Abilita ricerca IA e chat per questo libro",
"Start Indexing": "Avvia indicizzazione",
"Indexing book...": "Indicizzazione libro...",
"Preparing...": "Preparazione...",
"Delete this conversation?": "Eliminare questa conversazione?",
"No conversations yet": "Ancora nessuna conversazione",
"Start a new chat to ask questions about this book": "Avvia una nuova chat per fare domande su questo libro",
"Rename": "Rinomina",
"New Chat": "Nuova chat",
"Chat": "Chat",
"Please enter a model ID": "Inserisci un ID modello",
"Model not available or invalid": "Modello non disponibile o non valido",
"Failed to validate model": "Validazione modello fallita",
"Couldn't connect to Ollama. Is it running?": "Impossibile connettersi a Ollama. È in esecuzione?",
"Invalid API key or connection failed": "Chiave API non valida o connessione fallita",
"Connection failed": "Connessione fallita",
"AI Assistant": "Assistente IA",
"Enable AI Assistant": "Abilita assistente IA",
"Provider": "Fornitore",
"Ollama (Local)": "Ollama (Locale)",
"AI Gateway (Cloud)": "Gateway IA (Cloud)",
"Ollama Configuration": "Configurazione Ollama",
"Refresh Models": "Aggiorna modelli",
"AI Model": "Modello IA",
"No models detected": "Nessun modello rilevato",
"AI Gateway Configuration": "Configurazione gateway IA",
"Choose from a selection of high-quality, economical AI models. You can also bring your own model by selecting \"Custom Model\" below.": "Scegli tra una selezione di modelli IA di alta qualità ed economici. Puoi anche usare il tuo modello selezionando \"Modello personalizzato\" qui sotto.",
"API Key": "Chiave API",
"Get Key": "Ottieni chiave",
"Model": "Modello",
"Custom Model...": "Modello personalizzato...",
"Custom Model ID": "ID modello personalizzato",
"Validate": "Convalida",
"Model available": "Modello disponibile",
"Connection": "Connessione",
"Test Connection": "Testa connessione",
"Connected": "Connesso",
"Custom Colors": "Colori personalizzati",
"Color E-Ink Mode": "Modalità E-Ink a colori",
"Reading Ruler": "Righello di lettura",
"Enable Reading Ruler": "Abilita righello di lettura",
"Lines to Highlight": "Righe da evidenziare",
"Ruler Color": "Colore del righello",
"Command Palette": "Tavolozza dei comandi",
"Search settings and actions...": "Cerca impostazioni e azioni...",
"No results found for": "Nessun risultato trovato per",
"Type to search settings and actions": "Digita per cercare impostazioni e azioni",
"Recent": "Recenti",
"navigate": "naviga",
"select": "seleziona",
"close": "chiudi",
"Search Settings": "Cerca impostazioni",
"Page Margins": "Margini della pagina",
"AI Provider": "Fornitore AI",
"Ollama URL": "URL Ollama",
"Ollama Model": "Modello Ollama",
"AI Gateway Model": "Modello AI Gateway",
"Actions": "Azioni",
"Navigation": "Navigazione",
"Set status for {{count}} book(s)_one": "Imposta stato per {{count}} libro",
"Set status for {{count}} book(s)_other": "Imposta stato per {{count}} libri",
"Mark as Unread": "Segna come non letto",
"Mark as Finished": "Segna come finito",
"Finished": "Finito",
"Unread": "Non letto",
"Clear Status": "Cancella stato",
"Status": "Stato",
"Set status for {{count}} book(s)_many": "Imposta stato per {{count}} libri",
"Loading": "Caricamento...",
"Exit Paragraph Mode": "Esci dalla modalità paragrafo",
"Paragraph Mode": "Modalità paragrafo",
"Embedding Model": "Modello di incorporazione",
"{{count}} book(s) synced_one": "{{count}} libro sincronizzato",
"{{count}} book(s) synced_many": "{{count}} libri sincronizzati",
"{{count}} book(s) synced_other": "{{count}} libri sincronizzati",
"Unable to start RSVP": "Impossibile avviare RSVP",
"RSVP not supported for PDF": "RSVP non supportato per PDF",
"Select Chapter": "Seleziona capitolo",
"Context": "Contesto",
"Ready": "Pronto",
"Chapter Progress": "Progresso capitolo",
"words": "parole",
"{{time}} left": "{{time}} rimanenti",
"Reading progress": "Progresso lettura",
"Skip back 15 words": "Indietro di 15 parole",
"Back 15 words (Shift+Left)": "Indietro di 15 parole (Shift+Sinistra)",
"Pause (Space)": "Pausa (Spazio)",
"Play (Space)": "Riproduci (Spazio)",
"Skip forward 15 words": "Avanti di 15 parole",
"Forward 15 words (Shift+Right)": "Avanti di 15 parole (Shift+Destra)",
"Decrease speed": "Diminuisci velocità",
"Slower (Left/Down)": "Più lento (Sinistra/Giù)",
"Increase speed": "Aumenta velocità",
"Faster (Right/Up)": "Più veloce (Destra/Su)",
"Start RSVP Reading": "Avvia lettura RSVP",
"Choose where to start reading": "Scegli dove iniziare a leggere",
"From Chapter Start": "Dall'inizio del capitolo",
"Start reading from the beginning of the chapter": "Inizia a leggere dall'inizio del capitolo",
"Resume": "Riprendi",
"Continue from where you left off": "Continua da dove avevi interrotto",
"From Current Page": "Dalla pagina corrente",
"Start from where you are currently reading": "Inizia da dove stai leggendo",
"From Selection": "Dalla selezione",
"Speed Reading Mode": "Modalità lettura veloce",
"Scroll left": "Scorri a sinistra",
"Scroll right": "Scorri a destra",
"Library Sync Progress": "Avanzamento sincronizzazione libreria",
"Back to library": "Torna alla libreria",
"Group by...": "Raggruppa per...",
"Export as Plain Text": "Esporta come testo normale",
"Export as Markdown": "Esporta come Markdown",
"Show Page Navigation Buttons": "Tasti navigazione",
"Page {{number}}": "Pagina {{number}}",
"highlight": "evidenziazione",
"underline": "sottolineatura",
"squiggly": "ondulato",
"red": "rosso",
"violet": "viola",
"blue": "blu",
"green": "verde",
"yellow": "giallo",
"Select {{style}} style": "Seleziona lo stile {{style}}",
"Select {{color}} color": "Seleziona il colore {{color}}",
"Close Book": "Chiudi libro",
"Speed Reading": "Lettura veloce",
"Close Speed Reading": "Chiudi lettura veloce",
"Authors": "Autori",
"Books": "Libri",
"Groups": "Gruppi",
"Back to TTS Location": "Torna alla posizione TTS",
"Metadata": "Metadati",
"Image viewer": "Visualizzatore immagini",
"Previous Image": "Immagine precedente",
"Next Image": "Immagine successiva",
"Zoomed": "Zoomato",
"Zoom level": "Livello di zoom",
"Table viewer": "Visualizzatore tabelle",
"Unable to connect to Readwise. Please check your network connection.": "Impossibile connettersi a Readwise. Controlla la tua connessione di rete.",
"Invalid Readwise access token": "Token di accesso Readwise non valido",
"Disconnected from Readwise": "Disconnesso da Readwise",
"Never": "Mai",
"Sync Enabled": "Sincronizzazione abilitata",
"Disconnect": "Disconnetti",
"Connect your Readwise account to sync highlights.": "Connetti il tuo account Readwise per sincronizzare le evidenziazioni.",
"Get your access token at": "Ottieni il tuo token di accesso su",
"Access Token": "Token di accesso",
"Paste your Readwise access token": "Incolla il tuo token di accesso Readwise",
"Readwise Sync": "Sincronizzazione Readwise",
"Push Highlights": "Invia evidenziazioni",
"Highlights synced to Readwise": "Evidenziazioni sincronizzate su Readwise",
"Readwise sync failed: no internet connection": "Sincronizzazione Readwise fallita: nessuna connessione internet",
"Readwise sync failed: {{error}}": "Sincronizzazione Readwise fallita: {{error}}",
"System Screen Brightness": "Luminosità dello schermo del sistema",
"Page:": "Pagina:",
"Page: {{number}}": "Pagina: {{number}}",
"Annotation page number": "Numero di pagina dell'annotazione",
"Translating...": "Traduzione...",
"Show Battery Percentage": "Mostra percentuale batteria",
"Hide Scrollbar": "Nascondi barra di scorrimento",
"Skip to last reading position": "Vai all'ultima posizione di lettura",
"Show context": "Mostra contesto",
"Hide context": "Nascondi contesto",
"Decrease font size": "Riduci dimensione carattere",
"Increase font size": "Aumenta dimensione carattere",
"Focus": "Messa a fuoco",
"Theme color": "Colore del tema",
"Import failed": "Importazione fallita",
"Available Formatters:": "Formattatori disponibili:",
"Format date": "Formatta data",
"Markdown block quote (> per line)": "Citazione Markdown (> per riga)",
"Newlines to <br>": "A capo in <br>",
"Change case": "Cambia maiuscole/minuscole",
"Trim whitespace": "Rimuovi spazi",
"Truncate to n characters": "Tronca a n caratteri",
"Replace text": "Sostituisci testo",
"Fallback value": "Valore predefinito",
"Get length": "Ottieni lunghezza",
"First/last element": "Primo/ultimo elemento",
"Join array": "Unisci array",
"Backup failed: {{error}}": "Backup fallito: {{error}}",
"Select Backup": "Seleziona backup",
"Restore failed: {{error}}": "Ripristino fallito: {{error}}",
"Backup & Restore": "Backup e ripristino",
"Create a backup of your library or restore from a previous backup. Restoring will merge with your current library.": "Crea un backup della tua libreria o ripristina da un backup precedente. Il ripristino verrà unito alla tua libreria attuale.",
"Backup Library": "Backup libreria",
"Restore Library": "Ripristina libreria",
"Creating backup...": "Creazione backup...",
"Restoring library...": "Ripristino libreria...",
"{{current}} of {{total}} items": "{{current}} di {{total}} elementi",
"Backup completed successfully!": "Backup completato con successo!",
"Restore completed successfully!": "Ripristino completato con successo!",
"Your library has been saved to the selected location.": "La tua libreria è stata salvata nella posizione selezionata.",
"{{added}} books added, {{updated}} books updated.": "{{added}} libri aggiunti, {{updated}} libri aggiornati.",
"Operation failed": "Operazione fallita",
"Loading library...": "Caricamento libreria...",
"{{count}} books refreshed_one": "{{count}} libro aggiornato",
"{{count}} books refreshed_many": "{{count}} libri aggiornati",
"{{count}} books refreshed_other": "{{count}} libri aggiornati",
"Failed to refresh metadata": "Aggiornamento metadati fallito",
"Refresh Metadata": "Aggiorna metadati",
"Keyboard Shortcuts": "Scorciatoie da tastiera",
"View all keyboard shortcuts": "Visualizza tutte le scorciatoie",
"Switch Sidebar Tab": "Cambia scheda barra laterale",
"Toggle Notebook": "Mostra/nascondi blocco note",
"Search in Book": "Cerca nel libro",
"Toggle Scroll Mode": "Attiva/disattiva scorrimento",
"Toggle Select Mode": "Attiva/disattiva selezione",
"Toggle Bookmark": "Attiva/disattiva segnalibro",
"Toggle Text to Speech": "Attiva/disattiva sintesi vocale",
"Play / Pause TTS": "Riproduci / Pausa TTS",
"Toggle Paragraph Mode": "Attiva/disattiva modo paragrafo",
"Highlight Selection": "Evidenzia selezione",
"Underline Selection": "Sottolinea selezione",
"Annotate Selection": "Annota selezione",
"Search Selection": "Cerca selezione",
"Copy Selection": "Copia selezione",
"Translate Selection": "Traduci selezione",
"Dictionary Lookup": "Cerca nel dizionario",
"Read Aloud Selection": "Leggi la selezione ad alta voce",
"Proofread Selection": "Correggi selezione",
"Open Settings": "Apri impostazioni",
"Open Command Palette": "Apri tavolozza comandi",
"Show Keyboard Shortcuts": "Mostra scorciatoie da tastiera",
"Open Books": "Apri libri",
"Toggle Fullscreen": "Attiva/disattiva schermo intero",
"Close Window": "Chiudi finestra",
"Quit App": "Esci dall'app",
"Go Left / Previous Page": "Vai a sinistra / Pagina precedente",
"Go Right / Next Page": "Vai a destra / Pagina successiva",
"Go Up": "Vai su",
"Go Down": "Vai giù",
"Previous Chapter": "Capitolo precedente",
"Next Chapter": "Capitolo successivo",
"Scroll Half Page Down": "Scorri mezza pagina in basso",
"Scroll Half Page Up": "Scorri mezza pagina in alto",
"Save Note": "Salva nota",
"Single Section Scroll": "Scorrimento a sezione singola",
"General": "Generale",
"Text to Speech": "Sintesi vocale",
"Zoom": "Zoom",
"Window": "Finestra",
"Your Bookshelf": "La tua libreria",
"TTS": "Sintesi vocale",
"Media Info": "Info multimediali",
"Update Frequency": "Frequenza aggiornamento",
"Every Sentence": "Ogni frase",
"Every Paragraph": "Ogni paragrafo",
"Every Chapter": "Ogni capitolo",
"TTS Media Info Update Frequency": "Frequenza aggiornamento info multimediali TTS",
"Select reading speed": "Seleziona velocità di lettura",
"Drag to seek": "Trascina per cercare",
"Punctuation Delay": "Ritardo punteggiatura",
"Please confirm that this OPDS connection will be proxied through Readest servers on the web app before continuing.": "Conferma che questa connessione OPDS verrà instradata attraverso i server Readest nell'app web prima di continuare.",
"Custom Headers (optional)": "Intestazioni personalizzate (facoltativo)",
"Add one header per line using \"Header-Name: value\".": "Aggiungi un'intestazione per riga usando \"Header-Name: value\".",
"I understand this OPDS connection will be proxied through Readest servers on the web app. If I do not trust Readest with these credentials or headers, I should use the native app instead.": "Capisco che questa connessione OPDS verrà instradata attraverso i server Readest nell'app web. Se non mi fido di Readest con queste credenziali o intestazioni, dovrei usare l'app nativa.",
"Unable to connect to Hardcover. Please check your network connection.": "Impossibile connettersi a Hardcover. Controlla la connessione di rete.",
"Invalid Hardcover API token": "Token API Hardcover non valido",
"Disconnected from Hardcover": "Disconnesso da Hardcover",
"Connect your Hardcover account to sync reading progress and notes.": "Collega il tuo account Hardcover per sincronizzare i progressi di lettura e le note.",
"Get your API token from hardcover.app → Settings → API.": "Ottieni il tuo token API da hardcover.app → Impostazioni → API.",
"API Token": "Token API",
"Paste your Hardcover API token": "Incolla il tuo token API Hardcover",
"Hardcover Sync": "Sincronizzazione Hardcover",
"Push Notes": "Invia note",
"No annotations or excerpts to sync for this book.": "Nessuna annotazione o estratto da sincronizzare per questo libro.",
"Configure Hardcover in Settings first.": "Configura prima Hardcover nelle impostazioni.",
"No new Hardcover note changes to sync.": "Nessuna nuova modifica alle note Hardcover da sincronizzare.",
"Hardcover synced: {{inserted}} new, {{updated}} updated, {{skipped}} unchanged": "Hardcover sincronizzato: {{inserted}} nuovi, {{updated}} aggiornati, {{skipped}} invariati",
"Hardcover notes sync failed: {{error}}": "Sincronizzazione note Hardcover non riuscita: {{error}}",
"Reading progress synced to Hardcover": "Progressi di lettura sincronizzati con Hardcover",
"Hardcover progress sync failed: {{error}}": "Sincronizzazione progressi Hardcover non riuscita: {{error}}",
"ISBN": "ISBN",
"Keep existing source identifier": "Mantieni l'identificatore sorgente esistente",
"Toggle Toolbar": "Mostra/nascondi barra degli strumenti",
"Split Hyphens": "Dividi trattini",
"Apply Theme Colors to PDF": "Applica colori tema al PDF",
"Name": "Nome",
"Remove": "Rimuovi",
"Edit OPDS Catalog": "Modifica catalogo OPDS",
"Save Changes": "Salva modifiche",
"Use Book Layout": "Usa layout del libro",
"End of this section. Continue to the next.": "Fine di questa sezione. Continua alla successiva.",
"Auto-download": "Scaricamento automatico",
"Auto-download new items": "Scarica automaticamente i nuovi elementi",
"Automatically download new publications when the app syncs": "Scarica automaticamente le nuove pubblicazioni quando l'app si sincronizza",
"{{count}} new item(s) downloaded from OPDS_one": "{{count}} nuovo elemento scaricato da OPDS",
"{{count}} new item(s) downloaded from OPDS_many": "{{count}} nuovi elementi scaricati da OPDS",
"{{count}} new item(s) downloaded from OPDS_other": "{{count}} nuovi elementi scaricati da OPDS",
"Failed to sync {{count}} OPDS catalog(s)_one": "Impossibile sincronizzare {{count}} catalogo OPDS",
"Failed to sync {{count}} OPDS catalog(s)_many": "Impossibile sincronizzare {{count}} cataloghi OPDS",
"Failed to sync {{count}} OPDS catalog(s)_other": "Impossibile sincronizzare {{count}} cataloghi OPDS",
"Last synced {{when}}": "Ultima sincronizzazione {{when}}",
"Failed downloads": "Download non riusciti",
"No failed downloads": "Nessun download non riuscito",
"Attempts: {{count}}_one": "Tentativi: {{count}}",
"Attempts: {{count}}_many": "Tentativi: {{count}}",
"Attempts: {{count}}_other": "Tentativi: {{count}}",
"Skip": "Ignora",
"Skip all": "Ignora tutti",
"Retry all": "Riprova tutti",
"{{count}} failed_one": "{{count}} non riuscito",
"{{count}} failed_many": "{{count}} non riusciti",
"{{count}} failed_other": "{{count}} non riusciti",
"(none)": "(nessuno)",
"Identifiers": "Identificatori",
"Read (Stream)": "Leggi (Stream)",
"Failed to start stream": "Avvio dello stream non riuscito",
"No dictionaries enabled": "Nessun dizionario abilitato",
"Enable a dictionary in Settings → Language → Dictionaries.": "Abilita un dizionario in Impostazioni → Lingua → Dizionari.",
"Wiktionary": "Wikizionario",
"Drag to reorder": "Trascina per riordinare",
"Built-in": "Integrato",
"Bundle is missing on this device. Re-import to use it.": "Il pacchetto manca su questo dispositivo. Re-importalo per usarlo.",
"This dictionary format is not supported.": "Questo formato di dizionario non è supportato.",
"MDict": "MDict",
"StarDict": "StarDict",
"Imported {{count}} dictionary_one": "Importato {{count}} dizionario",
"Imported {{count}} dictionary_many": "Importati {{count}} dizionari",
"Imported {{count}} dictionary_other": "Importati {{count}} dizionari",
"Skipped incomplete bundles: {{names}}": "Pacchetti incompleti ignorati: {{names}}",
"Failed to import dictionary: {{message}}": "Importazione del dizionario non riuscita: {{message}}",
"Dictionaries": "Dizionari",
"Delete Dictionary": "Elimina dizionario",
"Importing…": "Importazione…",
"Import Dictionary": "Importa dizionario",
"No dictionaries available.": "Nessun dizionario disponibile.",
"StarDict bundles need .ifo, .idx, and .dict.dz files (.syn optional).": "I pacchetti StarDict richiedono file .ifo, .idx e .dict.dz (.syn opzionale).",
"Select all the bundle files together when importing.": "Seleziona tutti i file del pacchetto insieme durante limportazione.",
"Manage Dictionaries": "Gestisci dizionari",
"Select Dictionary Files": "Seleziona file del dizionario",
"Read on Wikipedia →": "Leggi su Wikipedia →",
"This link can't be opened": "Impossibile aprire questo collegamento",
"The annotation link is missing required information. The original link may have been truncated.": "Al collegamento di annotazione mancano informazioni necessarie. Il collegamento originale potrebbe essere stato troncato.",
"Go to Readest": "Vai a Readest",
"Open-source ebook reader for everyone, on every device.": "Lettore di e-book open source per tutti, su qualsiasi dispositivo.",
"Open in Readest": "Apri in Readest",
"If Readest didn't open automatically, choose an option below:": "Se Readest non si è aperto automaticamente, scegli un'opzione qui sotto:",
"Continue reading where you left off.": "Riprendi da dove avevi interrotto.",
"Readest logo": "Logo Readest",
"Opening Readest...": "Apertura di Readest…",
"Open in Readest app": "Apri nell'app Readest",
"Continue in browser": "Continua nel browser",
"Don't have Readest?": "Non hai Readest?",
"Book not in your library": "Libro non presente nella tua libreria",
"Share Book": "Condividi libro",
"Could not create share link": "Impossibile creare il link di condivisione",
"Link copied": "Link copiato",
"Could not copy link": "Impossibile copiare il link",
"Share revoked": "Condivisione revocata",
"Could not revoke share": "Impossibile revocare la condivisione",
"Uploading book…": "Caricamento libro…",
"Generating…": "Generazione…",
"Generate share link": "Genera link di condivisione",
"Share URL": "URL di condivisione",
"Copy link": "Copia link",
"Copied": "Copiato",
"Share via…": "Condividi tramite…",
"Expires {{date}}": "Scade il {{date}}",
"Revoking…": "Revoca in corso…",
"Revoke share": "Revoca condivisione",
"Sign in to share books": "Accedi per condividere libri",
"Expiring soon": "Scade presto",
"Missing share token": "Token di condivisione mancante",
"Could not add to your library": "Impossibile aggiungere alla tua libreria",
"This share link is no longer available": "Questo link di condivisione non è più disponibile",
"The original link may have expired or been revoked.": "Il link originale potrebbe essere scaduto o revocato.",
"Get Readest": "Ottieni Readest",
"Loading shared book…": "Caricamento libro condiviso…",
"Adding…": "Aggiunta in corso…",
"Add to my library": "Aggiungi alla mia libreria",
"Could not load your shares": "Impossibile caricare le tue condivisioni",
"Revoked": "Revocato",
"Expired": "Scaduto",
"Shared books": "Libri condivisi",
"You haven't shared any books yet": "Non hai ancora condiviso alcun libro",
"Open a book and tap Share to send it to a friend.": "Apri un libro e tocca Condividi per inviarlo a un amico.",
"{{count}} active_one": "{{count}} attivo",
"{{count}} active_many": "{{count}} attivi",
"{{count}} active_other": "{{count}} attivi",
"{{count}} downloads_one": "{{count}} download",
"{{count}} downloads_many": "{{count}} download",
"{{count}} downloads_other": "{{count}} download",
"starts at saved page": "inizia dalla pagina salvata",
"More actions": "Altre azioni",
"Loading…": "Caricamento…",
"Load more": "Carica altro",
"Could not load shared book": "Impossibile caricare il libro condiviso",
"The share link is missing required information.": "Al link di condivisione mancano informazioni necessarie.",
"Please check your connection and try again.": "Controlla la connessione e riprova.",
"Sign in to import shared books": "Accedi per importare libri condivisi",
"Already in your library": "Già nella tua libreria",
"Added to your library": "Aggiunto alla tua libreria",
"Could not import shared book": "Impossibile importare il libro condiviso",
"Manage Shared Links": "Gestisci link di condivisione",
"Expires in": "Scade tra",
"{{count}} days_one": "1 giorno",
"{{count}} days_many": "{{count}} giorni",
"{{count}} days_other": "{{count}} giorni",
"Expires in {{count}} days_one": "Scade tra 1 giorno",
"Expires in {{count}} days_many": "Scade tra {{count}} giorni",
"Expires in {{count}} days_other": "Scade tra {{count}} giorni",
"Expires in {{count}} hours_one": "Scade tra 1 ora",
"Expires in {{count}} hours_many": "Scade tra {{count}} ore",
"Expires in {{count}} hours_other": "Scade tra {{count}} ore",
"Open in app": "Apri nell'app",
"Shared with you": "Condiviso con te",
"Downloading… {{percent}}%": "Download in corso… {{percent}}%",
"Import progress": "Avanzamento dell'importazione",
"Share reading progress": "Condividi i progressi di lettura",
"Includes your reading progress": "Include i tuoi progressi di lettura",
"Synced {{time}}": "Sincronizzato {{time}}",
"Sync Info": "Info di sincronizzazione",
"Last Synced": "Ultima sincronizzazione",
"DICT": "DICT",
"Slob": "Slob",
"DICT bundles need a .index file and a .dict.dz file.": "I bundle DICT richiedono un file .index e un file .dict.dz.",
"Slob bundles need a .slob file.": "I bundle Slob richiedono un file .slob.",
"URL template must start with http(s):// and contain %WORD%.": "Il modello URL deve iniziare con http(s):// e contenere %WORD%.",
"Web": "Web",
"Add Web Search": "Aggiungi ricerca web",
"Edit Web Search": "Modifica ricerca web",
"e.g. Google": "es. Google",
"URL Template": "Modello URL",
"Use %WORD% where the looked-up word should appear.": "Usa %WORD% dove dovrebbe apparire la parola cercata.",
"Open the search result in your browser:": "Apri il risultato della ricerca nel tuo browser:",
"Open in {{name}}": "Apri in {{name}}",
"Book Fingerprint": "Impronta del libro",
"Search the web": "Cerca nel web",
"App deeplink (readest://)": "Deeplink app (readest://)",
"Universal web link (https://)": "Link web universale (https://)",
"Name cannot be empty.": "Il nome non può essere vuoto.",
"Replaced {{count}} existing dictionary_one": "Sostituito {{count}} dizionario esistente",
"Replaced {{count}} existing dictionary_many": "Sostituiti {{count}} dizionari esistenti",
"Replaced {{count}} existing dictionary_other": "Sostituiti {{count}} dizionari esistenti",
"Cancel Edit": "Annulla modifica",
"Edit Dictionary": "Modifica dizionario",
"MDict bundles use .mdx files; companion .mdd and .css files are optional.": "I pacchetti MDict usano file .mdx; i file .mdd e .css di accompagnamento sono facoltativi.",
"Dictionary name": "Nome del dizionario",
"This audio can't play here — the dictionary uses an outdated format. Try one with Opus, MP3, or WAV audio.": "Questo audio non può essere riprodotto qui — il dizionario usa un formato obsoleto. Prova un dizionario con audio Opus, MP3 o WAV.",
"File uploaded: {{title}}": "File caricato: {{title}}",
"File downloaded: {{title}}": "File scaricato: {{title}}",
"Deleted cloud copy of the file: {{title}}": "Copia cloud del file eliminata: {{title}}",
"Failed to upload file: {{title}}": "Caricamento file non riuscito: {{title}}",
"Failed to download file: {{title}}": "Download file non riuscito: {{title}}",
"Failed to delete cloud copy of the file: {{title}}": "Impossibile eliminare la copia cloud del file: {{title}}",
"{{percentage}}% used": "{{percentage}}% utilizzato",
"Set PIN…": "Imposta PIN…",
"Require a 4-digit PIN to open Readest": "Richiedi un PIN di 4 cifre per aprire Readest",
"Change PIN…": "Cambia PIN…",
"Disable PIN…": "Disattiva PIN…",
"Sync passphrase ready": "Passphrase di sincronizzazione pronta",
"This permanently deletes the encrypted credentials we sync (e.g., OPDS catalog passwords) on every device. Local copies are preserved. You will need to re-enter the sync passphrase or set a new one. Continue?": "Questa operazione elimina in modo permanente le credenziali crittografate che sincronizziamo (ad es. le password del catalogo OPDS) su ogni dispositivo. Le copie locali vengono conservate. Dovrai reinserire la passphrase di sincronizzazione o impostarne una nuova. Continuare?",
"Sync passphrase forgotten — all encrypted fields cleared": "Passphrase di sincronizzazione dimenticata — tutti i campi crittografati cancellati",
"Sync passphrase": "Passphrase di sincronizzazione",
"Set passphrase": "Imposta passphrase",
"Forgot passphrase": "Passphrase dimenticata",
"Incorrect PIN": "PIN errato",
"App locked": "App bloccata",
"Enter your PIN": "Inserisci il tuo PIN",
"Readest is locked. Enter your 4-digit PIN to continue.": "Readest è bloccato. Inserisci il tuo PIN di 4 cifre per continuare.",
"PIN code": "Codice PIN",
"Forgetting your PIN locks you out of this device. You'll need to clear the app's data to reset it.": "Dimenticare il PIN ti escluderà da questo dispositivo. Dovrai cancellare i dati dell'app per reimpostarlo.",
"Passphrase must be at least 8 characters": "La passphrase deve contenere almeno 8 caratteri",
"Passphrases do not match": "Le passphrase non corrispondono",
"Set sync passphrase": "Imposta passphrase di sincronizzazione",
"Enter sync passphrase": "Inserisci la passphrase di sincronizzazione",
"A sync passphrase encrypts your sensitive fields (like OPDS catalog credentials) before they sync. We never see this passphrase. Pick something memorable — there is no recovery without it.": "Una passphrase di sincronizzazione crittografa i tuoi campi sensibili (come le credenziali del catalogo OPDS) prima che vengano sincronizzati. Non vediamo mai questa passphrase. Scegli qualcosa di memorabile — senza di essa non c'è possibilità di recupero.",
"Enter the sync passphrase you set on another device to decrypt your synced credentials.": "Inserisci la passphrase di sincronizzazione impostata su un altro dispositivo per decrittografare le tue credenziali sincronizzate.",
"Confirm passphrase": "Conferma passphrase",
"Unlock": "Sblocca",
"Set PIN": "Imposta PIN",
"Change PIN": "Cambia PIN",
"Disable PIN": "Disattiva PIN",
"Enter your current PIN, then choose a new 4-digit PIN.": "Inserisci il tuo PIN attuale, quindi scegli un nuovo PIN di 4 cifre.",
"Enter your current PIN to disable the app lock.": "Inserisci il tuo PIN attuale per disattivare il blocco dell'app.",
"PIN must be {{length}} digits": "Il PIN deve avere {{length}} cifre",
"PINs do not match": "I PIN non corrispondono",
"Current PIN": "PIN attuale",
"New PIN": "Nuovo PIN",
"Confirm new PIN": "Conferma nuovo PIN",
"Manage Sync": "Gestisci sincronizzazione",
"Imported book files and library metadata": "File dei libri importati e metadati della libreria",
"Last-read position, bookmarks, and per-book preferences": "Ultima posizione di lettura, segnalibri e preferenze per libro",
"Highlights and notes": "Evidenziazioni e note",
"Fonts": "Caratteri",
"Custom font files": "File di caratteri personalizzati",
"Backgrounds": "Sfondi",
"Custom background textures": "Texture di sfondo personalizzate",
"OPDS catalogs": "Cataloghi OPDS",
"Saved catalog URLs and (encrypted) credentials": "URL dei cataloghi salvati e credenziali (cifrate)",
"Wrong sync passphrase — synced credentials could not be decrypted": "Passphrase di sincronizzazione errata — impossibile decifrare le credenziali sincronizzate",
"Sync passphrase data on the server was reset. Re-encrypting your credentials under the new passphrase…": "I dati della passphrase di sincronizzazione sul server sono stati reimpostati. Crittografia delle credenziali con la nuova passphrase in corso…",
"Failed to decrypt synced credentials": "Impossibile decifrare le credenziali sincronizzate",
"Imported dictionary bundles and settings": "Pacchetti di dizionari importati e impostazioni",
"Choose what syncs across your devices. Disabling a category stops this device from sending or receiving rows of that kind. Anything already on the server is left alone, re-enabling resumes from where you stopped.": "Scegli cosa sincronizzare tra i tuoi dispositivi. Disabilitare una categoria impedisce a questo dispositivo di inviare o ricevere voci di quel tipo. Quanto già presente sul server resta intatto, riabilitandola la sincronizzazione riprende da dove l'hai interrotta.",
"Data Sync": "Sincronizzazione dati",
"Manage Fonts": "Gestisci caratteri",
"App settings": "Impostazioni app",
"Theme, highlight colours, integrations (KOSync, Readwise, Hardcover), and dictionary order": "Tema, colori delle evidenziazioni, integrazioni (KOSync, Readwise, Hardcover) e ordine dei dizionari",
"Required while Dictionaries sync is enabled": "Necessario mentre la sincronizzazione dei Dizionari è attiva",
"Resets in {{hours}} hr {{minutes}} min": "Si reimposta tra {{hours}} h {{minutes}} min",
"Pick a 4-digit PIN. You will need to enter it every time you open Readest. There is no way to recover a forgotten PIN. Clearing the app data is the only way to reset it.": "Scegli un PIN di 4 cifre. Dovrai inserirlo ogni volta che apri Readest. Non c'è modo di recuperare un PIN dimenticato. Cancellare i dati dell'app è l'unico modo per reimpostarlo.",
"Credentials": "Credenziali",
"Tokens, usernames, and passwords for OPDS, KOReader, Hardcover, and Readwise. When disabled, credentials remain on this device only and are never uploaded.": "Token, nomi utente e password per OPDS, KOReader, Hardcover e Readwise. Quando disattivato, le credenziali restano solo su questo dispositivo e non vengono mai caricate.",
"Sensitive synced fields are encrypted before upload. Set a passphrase now or later when encryption is needed.": "I campi sincronizzati sensibili vengono cifrati prima del caricamento. Imposta una passphrase ora o più tardi, quando servirà la cifratura.",
"Saved to this account. You will be prompted for it when decrypting credentials.": "Salvata in questo account. Ti verrà richiesta quando occorrerà decifrare le credenziali.",
"Reading progress on this device differs from \"{{deviceName}}\".": "L'avanzamento di lettura su questo dispositivo è diverso da «{{deviceName}}».",
"This device": "Questo dispositivo",
"Red": "Rosso",
"Yellow": "Giallo",
"Green": "Verde",
"Blue": "Blu",
"Violet": "Viola",
"Edit color": "Modifica colore",
"Add custom color": "Aggiungi colore personalizzato",
"Add label": "Aggiungi etichetta",
"Choose color": "Scegli colore",
"Connected to Hardcover. Last synced {{time}}.": "Collegato a Hardcover. Ultima sincronizzazione {{time}}.",
"Integrations": "Integrazioni",
"Hardcover": "Hardcover",
"Connected to Readwise. Last synced {{time}}.": "Collegato a Readwise. Ultima sincronizzazione {{time}}.",
"Readwise": "Readwise",
"Browse and download books from online catalogs.": "Sfoglia e scarica libri dai cataloghi online.",
"Connected as {{user}}": "Collegato come {{user}}",
"Not connected": "Non collegato",
"{{count}} catalog_one": "{{count}} catalogo",
"{{count}} catalog_many": "{{count}} cataloghi",
"{{count}} catalog_other": "{{count}} cataloghi",
"No catalogs": "Nessun catalogo",
"Connect Readest to external services for sync, highlights, and catalogs.": "Collega Readest a servizi esterni per sincronizzazione, evidenziazioni e cataloghi.",
"Reading Sync": "Sincronizzazione lettura",
"Content Sources": "Fonti di contenuti",
"Scroll tabs": "Scorri schede",
"Catalog actions": "Azioni del catalogo",
"File Content": "Contenuto del file",
"Start your library": "Inizia la tua biblioteca",
"Pick a book from your device to add it to your library.": "Scegli un libro dal tuo dispositivo per aggiungerlo alla tua biblioteca.",
"Drop a book anywhere on this window, or pick one from your computer.": "Trascina un libro in qualsiasi punto di questa finestra, o sceglilo dal tuo computer.",
"Sign in to sync your library": "Accedi per sincronizzare la tua biblioteca"
}
@@ -102,11 +102,9 @@
"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": "この書籍に適用",
@@ -173,9 +171,6 @@
"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": "コミュニティサポート",
@@ -188,7 +183,6 @@
"RTL Direction": "RTL方向",
"Maximum Column Height": "最大列高",
"Maximum Column Width": "最大列幅",
"Continuous Scroll": "連続スクロール",
"Fullscreen": "全画面",
"No supported files found. Supported formats: {{formats}}": "サポートされているファイルが見つかりません。サポートされている形式:{{formats}}",
"Drop to Import Books": "書籍をインポートするにはドロップ",
@@ -302,7 +296,6 @@
"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}}分残り",
@@ -335,10 +328,10 @@
"Fit": "フィット",
"Reset {{settings}}": "{{settings}}をリセット",
"Reset Settings": "設定をリセット",
"{{count}} pages left in chapter_other": "{{count}} ページ残り",
"{{count}} pages left in chapter_other": "<0>{{count}}</0><1> ページ残り</1>",
"Show Remaining Pages": "残りを表示",
"Source Han Serif CN VF": "Source Han Serif",
"Huiwen-mincho": "Huiwen Mincho",
"Source Han Serif CN": "Source Han Serif",
"Huiwen-MinchoGBK": "Huiwen Mincho",
"KingHwa_OldSong": "KingHwa Song",
"Manage Subscription": "サブスクリプションを管理",
"Coming Soon": "近日公開",
@@ -462,14 +455,12 @@
"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": "変更のみを受信",
"Checksum Method": "チェックサム方式",
"File Content (recommended)": "ファイル内容(推奨)",
"File Name": "ファイル名",
"Device Name": "デバイス名",
"Connect to your KOReader Sync server.": "KOReader Syncサーバーに接続します。",
@@ -480,10 +471,7 @@
"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}}%)",
@@ -598,11 +586,10 @@
"Size": "サイズ",
"Cover": "カバー",
"Contain": "含む",
"{{number}} pages left in chapter": "章に{{number}}ページ残り",
"{{number}} pages left in chapter": "<1>章に</1><0>{{number}}</0><1>ページ残り</1>",
"Device": "デバイス",
"E-Ink Mode": "E-Inkモード",
"Highlight Colors": "ハイライトカラー",
"Auto Screen Brightness": "自動画面の明るさ",
"Pagination": "ページネーション",
"Disable Double Tap": "ダブルタップを無効にする",
"Tap to Paginate": "タップしてページを切り替える",
@@ -637,7 +624,6 @@
"Strikethrough": "取り消し線",
"Squiggly": "波線",
"Outline": "アウトライン",
"Save Current Color": "現在の色を保存",
"Quick Colors": "クイックカラー",
"Highlighter": "蛍光ペン",
"Save Book Cover": "本のカバーを保存",
@@ -695,9 +681,7 @@
"Validating...": "検証中...",
"View All": "すべて表示",
"Forward": "進む",
"OPDS Catalog": "OPDSカタログ",
"Home": "ホーム",
"Library": "ライブラリ",
"{{count}} items_other": "{{count}} 件",
"Download completed": "ダウンロード完了",
"Download failed": "ダウンロード失敗",
@@ -716,5 +700,692 @@
"Last": "最後",
"Cannot Load Page": "ページを読み込めません",
"An error occurred": "エラーが発生しました",
"Online Library": "オンラインライブラリ"
"Online Library": "オンラインライブラリ",
"URL must start with http:// or https://": "URLはhttp://またはhttps://で始まる必要があります",
"Title, Author, Tag, etc...": "タイトル、著者、タグなど...",
"Query": "クエリ",
"Subject": "件名",
"Enter {{terms}}": "{{terms}}を入力してください",
"No search results found": "検索結果が見つかりません",
"Failed to load OPDS feed: {{status}} {{statusText}}": "OPDSフィードの読み込みに失敗しました: {{status}} {{statusText}}",
"Search in {{title}}": "{{title}}内を検索",
"Manage Storage": "ストレージ管理",
"Failed to load files": "ファイルの読み込みに失敗しました",
"Deleted {{count}} file(s)_other": "{{count}} 件のファイルを削除しました",
"Failed to delete {{count}} file(s)_other": "{{count}} 件のファイルの削除に失敗しました",
"Failed to delete files": "ファイルの削除に失敗しました",
"Total Files": "ファイル合計",
"Total Size": "合計サイズ",
"Quota": "容量制限",
"Used": "使用済み",
"Files": "ファイル",
"Search files...": "ファイルを検索...",
"Newest First": "新しい順",
"Oldest First": "古い順",
"Largest First": "大きい順",
"Smallest First": "小さい順",
"Name A-Z": "名前 A-Z",
"Name Z-A": "名前 Z-A",
"{{count}} selected_other": "{{count}} 件選択済み",
"Delete Selected": "選択した項目を削除",
"Created": "作成日",
"No files found": "ファイルが見つかりません",
"No files uploaded yet": "まだファイルがアップロードされていません",
"files": "ファイル",
"Page {{current}} of {{total}}": "ページ {{current}} / {{total}}",
"Are you sure to delete {{count}} selected file(s)?_other": "選択した {{count}} 件のファイルを削除してもよろしいですか?",
"Cloud Storage Usage": "クラウドストレージ使用量",
"Rename Group": "グループ名を変更",
"From Directory": "ディレクトリから",
"Successfully imported {{count}} book(s)_other": "成功裏に{{count}}冊の本をインポートしました",
"Count": "件数",
"Start Page": "開始ページ",
"Search in OPDS Catalog...": "OPDSカタログ内を検索...",
"Please log in to use advanced TTS features": "高度なTTS機能を使用するにはログインしてください",
"Word limit of 30 words exceeded.": "30語の上限を超えています。",
"Proofread": "校正",
"Current selection": "現在の選択",
"All occurrences in this book": "この本内のすべての出現箇所",
"All occurrences in your library": "ライブラリ内のすべての出現箇所",
"Selected text:": "選択されたテキスト:",
"Replace with:": "置換後:",
"Enter text...": "テキストを入力…",
"Case sensitive:": "大文字と小文字を区別:",
"Scope:": "適用範囲:",
"Selection": "選択",
"Library": "ライブラリ",
"Yes": "はい",
"No": "いいえ",
"Proofread Replacement Rules": "校正置換ルール",
"Selected Text Rules": "選択テキストのルール",
"No selected text replacement rules": "選択されたテキストの置換ルールはありません",
"Book Specific Rules": "書籍別ルール",
"No book-level replacement rules": "書籍レベルの置換ルールはありません",
"Disable Quick Action": "クイックアクションを無効にする",
"Enable Quick Action on Selection": "選択時にクイックアクションを有効にする",
"None": "なし",
"Annotation Tools": "注釈ツール",
"Enable Quick Actions": "クイックアクションを有効にする",
"Quick Action": "クイックアクション",
"Copy to Notebook": "ノートにコピー",
"Copy text after selection": "選択後のテキストをコピー",
"Highlight text after selection": "選択後のテキストをハイライト",
"Annotate text after selection": "選択後のテキストに注釈を付ける",
"Search text after selection": "選択後のテキストを検索",
"Look up text in dictionary after selection": "選択後のテキストを辞書で調べる",
"Translate text after selection": "選択後のテキストを翻訳する",
"Read text aloud after selection": "選択後のテキストを音読する",
"Proofread text after selection": "選択後のテキストを校正する",
"{{activeCount}} active, {{pendingCount}} pending": "{{activeCount}} 進行中、{{pendingCount}} 待機中",
"{{failedCount}} failed": "{{failedCount}} 失敗",
"Waiting...": "待機中...",
"Failed": "失敗",
"Completed": "完了",
"Cancelled": "キャンセル",
"Retry": "再試行",
"Active": "進行中",
"Transfer Queue": "転送キュー",
"Upload All": "すべてアップロード",
"Download All": "すべてダウンロード",
"Resume Transfers": "転送を再開",
"Pause Transfers": "転送を一時停止",
"Pending": "待機中",
"No transfers": "転送なし",
"Retry All": "すべて再試行",
"Clear Completed": "完了を消去",
"Clear Failed": "失敗を消去",
"Upload queued: {{title}}": "アップロードをキューに追加: {{title}}",
"Download queued: {{title}}": "ダウンロードをキューに追加: {{title}}",
"Book not found in library": "ライブラリに本が見つかりません",
"Unknown error": "不明なエラー",
"Please log in to continue": "続行するにはログインしてください",
"Cloud File Transfers": "クラウドファイル転送",
"Show Search Results": "検索結果を表示",
"Search results for '{{term}}'": "「{{term}}」を含む結果",
"Close Search": "検索を閉じる",
"Previous Result": "前の結果",
"Next Result": "次の結果",
"Bookmarks": "ブックマーク",
"Annotations": "注釈",
"Show Results": "結果を表示",
"Clear search": "検索をクリア",
"Clear search history": "検索履歴をクリア",
"Tap to Toggle Footer": "タップでフッターを切り替え",
"Show Current Time": "現在時刻を表示",
"Use 24 Hour Clock": "24時間表示を使用",
"Show Current Battery Status": "バッテリー残量を表示",
"Exported successfully": "エクスポート成功",
"Book exported successfully.": "書籍のエクスポートに成功しました。",
"Failed to export the book.": "書籍のエクスポートに失敗しました。",
"Export Book": "書籍をエクスポート",
"Whole word:": "単語全体:",
"Date Published": "出版日",
"Only for TTS:": "TTSのみ:",
"Uploaded": "アップロード済み",
"Downloaded": "ダウンロード済み",
"Deleted": "削除済み",
"Note:": "ノート:",
"Time:": "時刻:",
"Format Options": "形式オプション",
"Export Date": "エクスポート日",
"Chapter Titles": "章タイトル",
"Chapter Separator": "章区切り",
"Highlights": "ハイライト",
"Note Date": "ノート日付",
"Advanced": "詳細設定",
"Hide": "非表示",
"Show": "表示",
"Use Custom Template": "カスタムテンプレート使用",
"Export Template": "エクスポートテンプレート",
"Template Syntax:": "テンプレート構文:",
"Insert value": "値を挿入",
"Format date (locale)": "日付形式(ロケール)",
"Format date (custom)": "日付形式(カスタム)",
"Conditional": "条件分岐",
"Loop": "ループ",
"Available Variables:": "利用可能な変数:",
"Book title": "本のタイトル",
"Book author": "著者",
"Export date": "エクスポート日",
"Array of chapters": "章の配列",
"Chapter title": "章タイトル",
"Array of annotations": "注釈の配列",
"Highlighted text": "ハイライトテキスト",
"Annotation note": "注釈ノート",
"Date Format Tokens:": "日付形式トークン:",
"Year (4 digits)": "年(4桁)",
"Month (01-12)": "月(01-12)",
"Day (01-31)": "日(01-31)",
"Hour (00-23)": "時(00-23)",
"Minute (00-59)": "分(00-59)",
"Second (00-59)": "秒(00-59)",
"Show Source": "ソース表示",
"No content to preview": "プレビューするコンテンツがありません",
"Export": "エクスポート",
"Set Timeout": "タイムアウト設定",
"Select Voice": "音声選択",
"Toggle Sticky Bottom TTS Bar": "TTSバー固定切替",
"Display what I'm reading on Discord": "読書中の本をDiscordに表示",
"Show on Discord": "Discordに表示",
"Instant {{action}}": "インスタント{{action}}",
"Instant {{action}} Disabled": "インスタント{{action}}を無効化",
"Annotation": "注釈",
"Reset Template": "テンプレートをリセット",
"Annotation style": "注釈スタイル",
"Annotation color": "注釈の色",
"Annotation time": "注釈時間",
"AI": "AI",
"Are you sure you want to re-index this book?": "この本を再インデックスしてもよろしいですか?",
"Enable AI in Settings": "設定でAIを有効にする",
"Index This Book": "この本をインデックス",
"Enable AI search and chat for this book": "この本のAI検索とチャットを有効にする",
"Start Indexing": "インデックス開始",
"Indexing book...": "本をインデックス中...",
"Preparing...": "準備中...",
"Delete this conversation?": "この会話を削除しますか?",
"No conversations yet": "まだ会話がありません",
"Start a new chat to ask questions about this book": "この本について質問するには新しいチャットを開始してください",
"Rename": "名前を変更",
"New Chat": "新しいチャット",
"Chat": "チャット",
"Please enter a model ID": "モデルIDを入力してください",
"Model not available or invalid": "モデルが利用できないか無効です",
"Failed to validate model": "モデルの検証に失敗しました",
"Couldn't connect to Ollama. Is it running?": "Ollamaに接続できませんでした。実行中ですか?",
"Invalid API key or connection failed": "APIキーが無効か接続に失敗しました",
"Connection failed": "接続に失敗しました",
"AI Assistant": "AIアシスタント",
"Enable AI Assistant": "AIアシスタントを有効にする",
"Provider": "プロバイダー",
"Ollama (Local)": "Ollama(ローカル)",
"AI Gateway (Cloud)": "AIゲートウェイ(クラウド)",
"Ollama Configuration": "Ollama設定",
"Refresh Models": "モデルを更新",
"AI Model": "AIモデル",
"No models detected": "モデルが検出されませんでした",
"AI Gateway Configuration": "AIゲートウェイ設定",
"Choose from a selection of high-quality, economical AI models. You can also bring your own model by selecting \"Custom Model\" below.": "高品質で経済的なAIモデルからお選びください。下の「カスタムモデル」を選択して独自のモデルを使用することもできます。",
"API Key": "APIキー",
"Get Key": "キーを取得",
"Model": "モデル",
"Custom Model...": "カスタムモデル...",
"Custom Model ID": "カスタムモデルID",
"Validate": "検証",
"Model available": "モデルが利用可能",
"Connection": "接続",
"Test Connection": "接続をテスト",
"Connected": "接続済み",
"Custom Colors": "カスタムカラー",
"Color E-Ink Mode": "カラー電子ペーパーモード",
"Reading Ruler": "リーディングルーラー",
"Enable Reading Ruler": "リーディングルーラーを有効にする",
"Lines to Highlight": "ハイライトする行数",
"Ruler Color": "ルーラーの色",
"Command Palette": "コマンドパレット",
"Search settings and actions...": "設定とアクションを検索...",
"No results found for": "の結果が見つかりませんでした",
"Type to search settings and actions": "入力して設定とアクションを検索",
"Recent": "最近使った項目",
"navigate": "ナビゲート",
"select": "選択",
"close": "閉じる",
"Search Settings": "設定を検索",
"Page Margins": "ページ余白",
"AI Provider": "AIプロバイダー",
"Ollama URL": "OllamaのURL",
"Ollama Model": "Ollamaモデル",
"AI Gateway Model": "AI Gatewayモデル",
"Actions": "アクション",
"Navigation": "ナビゲーション",
"Set status for {{count}} book(s)_other": "{{count}} 冊の本のステータスを設定",
"Mark as Unread": "未読にする",
"Mark as Finished": "読了にする",
"Finished": "読了",
"Unread": "未読",
"Clear Status": "ステータスをクリア",
"Status": "ステータス",
"Loading": "読み込み中",
"Exit Paragraph Mode": "段落モードを終了",
"Paragraph Mode": "段落モード",
"Embedding Model": "埋め込みモデル",
"{{count}} book(s) synced_other": "{{count}} 冊の本が同期されました",
"Unable to start RSVP": "RSVPを起動できません",
"RSVP not supported for PDF": "PDFはRSVPに対応していません",
"Select Chapter": "章を選択",
"Context": "コンテキスト",
"Ready": "準備完了",
"Chapter Progress": "章の進捗",
"words": "単語",
"{{time}} left": "残り {{time}}",
"Reading progress": "読書の進捗",
"Skip back 15 words": "15単語戻る",
"Back 15 words (Shift+Left)": "15単語戻る (Shift+左)",
"Pause (Space)": "一時停止 (Space)",
"Play (Space)": "再生 (Space)",
"Skip forward 15 words": "15単語進む",
"Forward 15 words (Shift+Right)": "15単語進む (Shift+右)",
"Decrease speed": "速度を下げる",
"Slower (Left/Down)": "低速 (左/下)",
"Increase speed": "速度を上げる",
"Faster (Right/Up)": "高速 (右/上)",
"Start RSVP Reading": "RSVP読書を開始",
"Choose where to start reading": "読書を開始する場所を選択",
"From Chapter Start": "章の最初から",
"Start reading from the beginning of the chapter": "この章の最初から読み直す",
"Resume": "再開",
"Continue from where you left off": "中断した場所から再開する",
"From Current Page": "現在のページから",
"Start from where you are currently reading": "現在読んでいる場所から開始する",
"From Selection": "選択範囲から",
"Speed Reading Mode": "速読モード",
"Scroll left": "左にスクロール",
"Scroll right": "右にスクロール",
"Library Sync Progress": "ライブラリ同期の進捗",
"Back to library": "ライブラリに戻る",
"Group by...": "グループ化...",
"Export as Plain Text": "プレーンテキストとして書き出し",
"Export as Markdown": "Markdownとして書き出し",
"Show Page Navigation Buttons": "ナビゲーションボタンを表示",
"Page {{number}}": "{{number}} ページ",
"highlight": "ハイライト",
"underline": "下線",
"squiggly": "波線",
"red": "赤",
"violet": "すみれ色",
"blue": "青",
"green": "緑",
"yellow": "黄色",
"Select {{style}} style": "スタイル {{style}} を選択",
"Select {{color}} color": "色 {{color}} を選択",
"Close Book": "本を閉じる",
"Speed Reading": "速読",
"Close Speed Reading": "速読を閉じる",
"Authors": "著者",
"Books": "書籍",
"Groups": "グループ",
"Back to TTS Location": "TTSの位置に戻る",
"Metadata": "メタデータ",
"Image viewer": "画像ビューア",
"Previous Image": "前の画像",
"Next Image": "次の画像",
"Zoomed": "ズーム済み",
"Zoom level": "ズームレベル",
"Table viewer": "テーブルビューア",
"Unable to connect to Readwise. Please check your network connection.": "Readwise に接続できません。ネットワーク接続を確認してください。",
"Invalid Readwise access token": "無効な Readwise アクセストークン",
"Disconnected from Readwise": "Readwise から切断されました",
"Never": "一度もなし",
"Sync Enabled": "同期有効",
"Disconnect": "切断",
"Connect your Readwise account to sync highlights.": "ハイライトを同期するには Readwise アカウントを接続してください。",
"Get your access token at": "アクセストークンの取得先:",
"Access Token": "アクセストークン",
"Paste your Readwise access token": "Readwise アクセストークンを貼り付けてください",
"Readwise Sync": "Readwise 同期",
"Push Highlights": "ハイライトをプッシュ",
"Highlights synced to Readwise": "ハイライトが Readwise に同期されました",
"Readwise sync failed: no internet connection": "Readwise 同期失敗:インターネット接続がありません",
"Readwise sync failed: {{error}}": "Readwise 同期失敗:{{error}}",
"System Screen Brightness": "システムの画面の明るさ",
"Page:": "ページ:",
"Page: {{number}}": "ページ: {{number}}",
"Annotation page number": "注釈のページ番号",
"Translating...": "翻訳中...",
"Show Battery Percentage": "バッテリー残量を表示",
"Hide Scrollbar": "スクロールバーを非表示",
"Skip to last reading position": "最後の読書位置へ移動",
"Show context": "コンテキストを表示",
"Hide context": "コンテキストを非表示",
"Decrease font size": "文字サイズを縮小",
"Increase font size": "文字サイズを拡大",
"Focus": "フォーカス",
"Theme color": "テーマカラー",
"Import failed": "インポートに失敗しました",
"Available Formatters:": "利用可能なフォーマッター:",
"Format date": "日付のフォーマット",
"Markdown block quote (> per line)": "Markdown引用ブロック (行ごとに >)",
"Newlines to <br>": "改行を <br> に変換",
"Change case": "大文字/小文字の変更",
"Trim whitespace": "空白を除去",
"Truncate to n characters": "n文字に切り詰め",
"Replace text": "テキストを置換",
"Fallback value": "フォールバック値",
"Get length": "長さを取得",
"First/last element": "最初/最後の要素",
"Join array": "配列を結合",
"Backup failed: {{error}}": "バックアップに失敗しました: {{error}}",
"Select Backup": "バックアップを選択",
"Restore failed: {{error}}": "復元に失敗しました: {{error}}",
"Backup & Restore": "バックアップと復元",
"Create a backup of your library or restore from a previous backup. Restoring will merge with your current library.": "ライブラリのバックアップを作成するか、以前のバックアップから復元します。復元は現在のライブラリとマージされます。",
"Backup Library": "ライブラリをバックアップ",
"Restore Library": "ライブラリを復元",
"Creating backup...": "バックアップを作成中...",
"Restoring library...": "ライブラリを復元中...",
"{{current}} of {{total}} items": "{{current}} / {{total}} 件",
"Backup completed successfully!": "バックアップが完了しました!",
"Restore completed successfully!": "復元が完了しました!",
"Your library has been saved to the selected location.": "ライブラリが選択した場所に保存されました。",
"{{added}} books added, {{updated}} books updated.": "{{added}}冊追加、{{updated}}冊更新されました。",
"Operation failed": "操作に失敗しました",
"Loading library...": "ライブラリを読み込み中...",
"{{count}} books refreshed_other": "{{count}}冊のメタデータを更新しました",
"Failed to refresh metadata": "メタデータの更新に失敗しました",
"Refresh Metadata": "メタデータを更新",
"Keyboard Shortcuts": "キーボードショートカット",
"View all keyboard shortcuts": "すべてのキーボードショートカットを表示",
"Switch Sidebar Tab": "サイドバータブを切り替え",
"Toggle Notebook": "ノートブックの表示/非表示",
"Search in Book": "本の中を検索",
"Toggle Scroll Mode": "スクロールモードの切り替え",
"Toggle Select Mode": "選択モードの切り替え",
"Toggle Bookmark": "ブックマークの切り替え",
"Toggle Text to Speech": "音声読み上げの切り替え",
"Play / Pause TTS": "読み上げ再生 / 一時停止",
"Toggle Paragraph Mode": "段落モードの切り替え",
"Highlight Selection": "選択範囲をハイライト",
"Underline Selection": "選択範囲に下線",
"Annotate Selection": "選択範囲に注釈",
"Search Selection": "選択範囲を検索",
"Copy Selection": "選択範囲をコピー",
"Translate Selection": "選択範囲を翻訳",
"Dictionary Lookup": "辞書で調べる",
"Read Aloud Selection": "選択範囲を読み上げ",
"Proofread Selection": "選択範囲を校正",
"Open Settings": "設定を開く",
"Open Command Palette": "コマンドパレットを開く",
"Show Keyboard Shortcuts": "キーボードショートカットを表示",
"Open Books": "本を開く",
"Toggle Fullscreen": "フルスクリーンの切り替え",
"Close Window": "ウィンドウを閉じる",
"Quit App": "アプリを終了",
"Go Left / Previous Page": "左へ / 前のページ",
"Go Right / Next Page": "右へ / 次のページ",
"Go Up": "上へ",
"Go Down": "下へ",
"Previous Chapter": "前の章",
"Next Chapter": "次の章",
"Scroll Half Page Down": "半ページ下にスクロール",
"Scroll Half Page Up": "半ページ上にスクロール",
"Save Note": "メモを保存",
"Single Section Scroll": "単一セクションスクロール",
"General": "一般",
"Text to Speech": "テキスト読み上げ",
"Zoom": "ズーム",
"Window": "ウィンドウ",
"Your Bookshelf": "あなたの本棚",
"TTS": "読み上げ",
"Media Info": "メディア情報",
"Update Frequency": "更新頻度",
"Every Sentence": "文ごと",
"Every Paragraph": "段落ごと",
"Every Chapter": "章ごと",
"TTS Media Info Update Frequency": "TTSメディア情報の更新頻度",
"Select reading speed": "読み上げ速度を選択",
"Drag to seek": "ドラッグしてシーク",
"Punctuation Delay": "句読点の遅延",
"Please confirm that this OPDS connection will be proxied through Readest servers on the web app before continuing.": "続行する前に、このOPDS接続がWebアプリのReadestサーバーを経由してプロキシされることを確認してください。",
"Custom Headers (optional)": "カスタムヘッダー(任意)",
"Add one header per line using \"Header-Name: value\".": "「Header-Name: value」の形式で1行に1つのヘッダーを追加してください。",
"I understand this OPDS connection will be proxied through Readest servers on the web app. If I do not trust Readest with these credentials or headers, I should use the native app instead.": "このOPDS接続がWebアプリのReadestサーバーを経由してプロキシされることを理解しています。これらの認証情報やヘッダーについてReadestを信頼しない場合は、ネイティブアプリを使用してください。",
"Unable to connect to Hardcover. Please check your network connection.": "Hardcoverに接続できません。ネットワーク接続を確認してください。",
"Invalid Hardcover API token": "Hardcover APIトークンが無効です",
"Disconnected from Hardcover": "Hardcoverから切断されました",
"Connect your Hardcover account to sync reading progress and notes.": "Hardcoverアカウントを接続して、読書の進捗やメモを同期しましょう。",
"Get your API token from hardcover.app → Settings → API.": "hardcover.app → 設定 → APIからAPIトークンを取得してください。",
"API Token": "APIトークン",
"Paste your Hardcover API token": "HardcoverのAPIトークンを貼り付け",
"Hardcover Sync": "Hardcover同期",
"Push Notes": "メモを送信",
"No annotations or excerpts to sync for this book.": "この本に同期する注釈や抜粋がありません。",
"Configure Hardcover in Settings first.": "まず設定でHardcoverを構成してください。",
"No new Hardcover note changes to sync.": "同期するHardcoverメモの新しい変更はありません。",
"Hardcover synced: {{inserted}} new, {{updated}} updated, {{skipped}} unchanged": "Hardcover同期完了: {{inserted}}件追加、{{updated}}件更新、{{skipped}}件変更なし",
"Hardcover notes sync failed: {{error}}": "Hardcoverメモの同期に失敗: {{error}}",
"Reading progress synced to Hardcover": "読書の進捗がHardcoverに同期されました",
"Hardcover progress sync failed: {{error}}": "Hardcover進捗の同期に失敗: {{error}}",
"ISBN": "ISBN",
"Keep existing source identifier": "既存のソース識別子を保持",
"Toggle Toolbar": "ツールバーの表示切替",
"Split Hyphens": "ハイフンで分割",
"Apply Theme Colors to PDF": "テーマカラーをPDFに適用",
"Name": "名前",
"Remove": "削除",
"Edit OPDS Catalog": "OPDSカタログを編集",
"Save Changes": "変更を保存",
"Use Book Layout": "書籍のレイアウトを使用",
"End of this section. Continue to the next.": "このセクションの終わりです。次のセクションへ進みます。",
"Auto-download": "自動ダウンロード",
"Auto-download new items": "新着アイテムを自動ダウンロード",
"Automatically download new publications when the app syncs": "アプリの同期時に新しい出版物を自動的にダウンロードします",
"{{count}} new item(s) downloaded from OPDS_other": "OPDSから{{count}}件の新着アイテムをダウンロードしました",
"Failed to sync {{count}} OPDS catalog(s)_other": "OPDSカタログ{{count}}件の同期に失敗しました",
"Last synced {{when}}": "最終同期 {{when}}",
"Failed downloads": "失敗したダウンロード",
"No failed downloads": "失敗したダウンロードはありません",
"Attempts: {{count}}_other": "試行回数: {{count}}",
"Skip": "スキップ",
"Skip all": "すべてスキップ",
"Retry all": "すべて再試行",
"{{count}} failed_other": "{{count}}件失敗",
"(none)": "(なし)",
"Identifiers": "識別子",
"Read (Stream)": "読む(ストリーム)",
"Failed to start stream": "ストリームの開始に失敗しました",
"No dictionaries enabled": "有効な辞書がありません",
"Enable a dictionary in Settings → Language → Dictionaries.": "設定 → 言語 → 辞書 で辞書を有効にしてください。",
"Wiktionary": "ウィクショナリー",
"Drag to reorder": "ドラッグで並べ替え",
"Built-in": "組み込み",
"Bundle is missing on this device. Re-import to use it.": "この端末にバンドルがありません。再度インポートしてご利用ください。",
"This dictionary format is not supported.": "この辞書形式はサポートされていません。",
"MDict": "MDict",
"StarDict": "StarDict",
"Imported {{count}} dictionary_other": "{{count}} 件の辞書をインポートしました",
"Skipped incomplete bundles: {{names}}": "不完全なバンドルをスキップしました: {{names}}",
"Failed to import dictionary: {{message}}": "辞書のインポートに失敗しました: {{message}}",
"Dictionaries": "辞書",
"Delete Dictionary": "辞書を削除",
"Importing…": "インポート中…",
"Import Dictionary": "辞書をインポート",
"No dictionaries available.": "利用可能な辞書はありません。",
"StarDict bundles need .ifo, .idx, and .dict.dz files (.syn optional).": "StarDict バンドルには .ifo、.idx、.dict.dz ファイルが必要です(.syn は任意)。",
"Select all the bundle files together when importing.": "インポート時はバンドルのファイルをまとめて選択してください。",
"Manage Dictionaries": "辞書を管理",
"Select Dictionary Files": "辞書ファイルを選択",
"Read on Wikipedia →": "Wikipedia で読む →",
"This link can't be opened": "このリンクは開けません",
"The annotation link is missing required information. The original link may have been truncated.": "注釈リンクに必要な情報が不足しています。元のリンクが途中で切れている可能性があります。",
"Go to Readest": "Readest を開く",
"Open-source ebook reader for everyone, on every device.": "誰もが、どんなデバイスでも使えるオープンソースの電子書籍リーダー。",
"Open in Readest": "Readest で開く",
"If Readest didn't open automatically, choose an option below:": "Readest が自動で開かなかった場合は、以下から選んでください:",
"Continue reading where you left off.": "前回の続きから読み始めましょう。",
"Readest logo": "Readest のロゴ",
"Opening Readest...": "Readest を開いています…",
"Open in Readest app": "Readest アプリで開く",
"Continue in browser": "ブラウザーで続ける",
"Don't have Readest?": "Readest をお持ちでない方は",
"Book not in your library": "この本はライブラリにありません",
"Share Book": "本を共有",
"Could not create share link": "共有リンクを作成できませんでした",
"Link copied": "リンクをコピーしました",
"Could not copy link": "リンクをコピーできませんでした",
"Share revoked": "共有を取り消しました",
"Could not revoke share": "共有を取り消せませんでした",
"Uploading book…": "本をアップロード中…",
"Generating…": "生成中…",
"Generate share link": "共有リンクを作成",
"Share URL": "共有URL",
"Copy link": "リンクをコピー",
"Copied": "コピーしました",
"Share via…": "共有…",
"Expires {{date}}": "{{date}}に期限切れ",
"Revoking…": "取り消し中…",
"Revoke share": "共有を取り消す",
"Sign in to share books": "本を共有するにはサインインしてください",
"Expiring soon": "まもなく期限切れ",
"Missing share token": "共有トークンが見つかりません",
"Could not add to your library": "ライブラリに追加できませんでした",
"This share link is no longer available": "この共有リンクはもう利用できません",
"The original link may have expired or been revoked.": "元のリンクは期限切れか取り消された可能性があります。",
"Get Readest": "Readest を入手",
"Loading shared book…": "共有された本を読み込み中…",
"Adding…": "追加中…",
"Add to my library": "ライブラリに追加",
"Could not load your shares": "共有を読み込めませんでした",
"Revoked": "取り消し済み",
"Expired": "期限切れ",
"Shared books": "共有された本",
"You haven't shared any books yet": "まだ本を共有していません",
"Open a book and tap Share to send it to a friend.": "本を開き、共有をタップして友達に送りましょう。",
"{{count}} active_other": "{{count}} 件アクティブ",
"{{count}} downloads_other": "{{count}} 件のダウンロード",
"starts at saved page": "保存されたページから開始",
"More actions": "その他の操作",
"Loading…": "読み込み中…",
"Load more": "さらに読み込む",
"Could not load shared book": "共有された本を読み込めませんでした",
"The share link is missing required information.": "共有リンクに必要な情報が含まれていません。",
"Please check your connection and try again.": "接続を確認してもう一度お試しください。",
"Sign in to import shared books": "共有された本を取り込むにはサインインしてください",
"Already in your library": "すでにライブラリにあります",
"Added to your library": "ライブラリに追加しました",
"Could not import shared book": "共有された本を取り込めませんでした",
"Manage Shared Links": "共有リンクを管理",
"Expires in": "有効期限",
"{{count}} days_other": "{{count}}日",
"Expires in {{count}} days_other": "{{count}}日で期限切れ",
"Expires in {{count}} hours_other": "{{count}}時間で期限切れ",
"Open in app": "アプリで開く",
"Shared with you": "あなたへの共有",
"Downloading… {{percent}}%": "ダウンロード中… {{percent}}%",
"Import progress": "インポートの進行状況",
"Share reading progress": "読書の進捗を共有",
"Includes your reading progress": "読書の進捗を含みます",
"Synced {{time}}": "{{time}}に同期",
"Sync Info": "同期情報",
"Last Synced": "最終同期",
"DICT": "DICT",
"Slob": "Slob",
"DICT bundles need a .index file and a .dict.dz file.": "DICT バンドルには .index ファイルと .dict.dz ファイルが必要です。",
"Slob bundles need a .slob file.": "Slob バンドルには .slob ファイルが必要です。",
"URL template must start with http(s):// and contain %WORD%.": "URL テンプレートは http(s):// で始まり、%WORD% を含む必要があります。",
"Web": "ウェブ",
"Add Web Search": "ウェブ検索を追加",
"Edit Web Search": "ウェブ検索を編集",
"e.g. Google": "例: Google",
"URL Template": "URL テンプレート",
"Use %WORD% where the looked-up word should appear.": "調べる単語が表示される場所に %WORD% を使用してください。",
"Open the search result in your browser:": "検索結果をブラウザで開く:",
"Open in {{name}}": "{{name}} で開く",
"Book Fingerprint": "書籍の指紋",
"Search the web": "ウェブで検索",
"App deeplink (readest://)": "アプリディープリンク (readest://)",
"Universal web link (https://)": "ユニバーサルウェブリンク (https://)",
"Name cannot be empty.": "名前を空にすることはできません。",
"Replaced {{count}} existing dictionary_other": "既存の辞書 {{count}} 件を置き換えました",
"Cancel Edit": "編集をキャンセル",
"Edit Dictionary": "辞書を編集",
"MDict bundles use .mdx files; companion .mdd and .css files are optional.": "MDict バンドルは .mdx ファイルを使用します。付属の .mdd および .css ファイルは任意です。",
"Dictionary name": "辞書名",
"This audio can't play here — the dictionary uses an outdated format. Try one with Opus, MP3, or WAV audio.": "この音声はここで再生できません — 辞書が古い形式を使用しています。Opus、MP3、または WAV 音声を使用する辞書を試してください。",
"File uploaded: {{title}}": "ファイルがアップロードされました:{{title}}",
"File downloaded: {{title}}": "ファイルがダウンロードされました:{{title}}",
"Deleted cloud copy of the file: {{title}}": "ファイルのクラウドコピーを削除しました:{{title}}",
"Failed to upload file: {{title}}": "ファイルのアップロードに失敗しました:{{title}}",
"Failed to download file: {{title}}": "ファイルのダウンロードに失敗しました:{{title}}",
"Failed to delete cloud copy of the file: {{title}}": "ファイルのクラウドコピーの削除に失敗しました:{{title}}",
"{{percentage}}% used": "{{percentage}}% 使用済み",
"Set PIN…": "PINを設定…",
"Require a 4-digit PIN to open Readest": "Readestを開くのに4桁のPINを要求する",
"Change PIN…": "PINを変更…",
"Disable PIN…": "PINを無効化…",
"Sync passphrase ready": "同期パスフレーズの準備ができました",
"This permanently deletes the encrypted credentials we sync (e.g., OPDS catalog passwords) on every device. Local copies are preserved. You will need to re-enter the sync passphrase or set a new one. Continue?": "これにより、各デバイスで同期している暗号化された資格情報(OPDS カタログのパスワードなど)が永続的に削除されます。ローカルのコピーは保持されます。同期パスフレーズを再入力するか、新しく設定する必要があります。続行しますか?",
"Sync passphrase forgotten — all encrypted fields cleared": "同期パスフレーズを忘れました — 暗号化されたフィールドはすべてクリアされました",
"Sync passphrase": "同期パスフレーズ",
"Set passphrase": "パスフレーズを設定",
"Forgot passphrase": "パスフレーズを忘れました",
"Incorrect PIN": "PINが正しくありません",
"App locked": "アプリがロックされています",
"Enter your PIN": "PINを入力してください",
"Readest is locked. Enter your 4-digit PIN to continue.": "Readestはロックされています。続行するには4桁のPINを入力してください。",
"PIN code": "PINコード",
"Forgetting your PIN locks you out of this device. You'll need to clear the app's data to reset it.": "PINを忘れるとこのデバイスから締め出されます。リセットするにはアプリのデータを消去する必要があります。",
"Passphrase must be at least 8 characters": "パスフレーズは 8 文字以上にしてください",
"Passphrases do not match": "パスフレーズが一致しません",
"Set sync passphrase": "同期パスフレーズを設定",
"Enter sync passphrase": "同期パスフレーズを入力",
"A sync passphrase encrypts your sensitive fields (like OPDS catalog credentials) before they sync. We never see this passphrase. Pick something memorable — there is no recovery without it.": "同期パスフレーズは、機密フィールド(OPDS カタログの認証情報など)を同期する前に暗号化します。このパスフレーズを当社が知ることはありません。覚えやすいものを選んでください — これがないと復元できません。",
"Enter the sync passphrase you set on another device to decrypt your synced credentials.": "別のデバイスで設定した同期パスフレーズを入力して、同期された資格情報を復号化します。",
"Confirm passphrase": "パスフレーズを確認",
"Unlock": "ロック解除",
"Set PIN": "PINを設定",
"Change PIN": "PINを変更",
"Disable PIN": "PINを無効化",
"Enter your current PIN, then choose a new 4-digit PIN.": "現在のPINを入力してから、新しい4桁のPINを選んでください。",
"Enter your current PIN to disable the app lock.": "アプリのロックを解除するには現在のPINを入力してください。",
"PIN must be {{length}} digits": "PINは{{length}}桁である必要があります",
"PINs do not match": "PINが一致しません",
"Current PIN": "現在のPIN",
"New PIN": "新しいPIN",
"Confirm new PIN": "新しいPINの確認",
"Manage Sync": "同期を管理",
"Imported book files and library metadata": "インポートした書籍ファイルとライブラリのメタデータ",
"Last-read position, bookmarks, and per-book preferences": "最終閲覧位置、ブックマーク、書籍ごとの設定",
"Highlights and notes": "ハイライトとノート",
"Fonts": "フォント",
"Custom font files": "カスタムフォントファイル",
"Backgrounds": "背景",
"Custom background textures": "カスタム背景テクスチャ",
"OPDS catalogs": "OPDS カタログ",
"Saved catalog URLs and (encrypted) credentials": "保存済みのカタログ URL と(暗号化された)認証情報",
"Wrong sync passphrase — synced credentials could not be decrypted": "同期パスフレーズが違います — 同期された認証情報を復号できませんでした",
"Sync passphrase data on the server was reset. Re-encrypting your credentials under the new passphrase…": "サーバーの同期パスフレーズデータがリセットされました。新しいパスフレーズで認証情報を再暗号化しています…",
"Failed to decrypt synced credentials": "同期された認証情報を復号できませんでした",
"Imported dictionary bundles and settings": "インポートした辞書バンドルと設定",
"Choose what syncs across your devices. Disabling a category stops this device from sending or receiving rows of that kind. Anything already on the server is left alone, re-enabling resumes from where you stopped.": "デバイス間で同期する項目を選択します。カテゴリを無効にすると、このデバイスはその種類のデータを送受信しなくなります。サーバーに既にあるデータはそのまま残り、再度有効にすると停止した位置から再開します。",
"Data Sync": "データ同期",
"Manage Fonts": "フォントを管理",
"App settings": "アプリ設定",
"Theme, highlight colours, integrations (KOSync, Readwise, Hardcover), and dictionary order": "テーマ、ハイライトの色、連携 (KOSync、Readwise、Hardcover)、辞書の順序",
"Required while Dictionaries sync is enabled": "辞書の同期が有効な間は必須",
"Resets in {{hours}} hr {{minutes}} min": "{{hours}}時間{{minutes}}分でリセット",
"Pick a 4-digit PIN. You will need to enter it every time you open Readest. There is no way to recover a forgotten PIN. Clearing the app data is the only way to reset it.": "4桁のPINを設定してください。Readestを開くたびに入力する必要があります。忘れたPINを復元する方法はありません。アプリのデータを消去することがリセットする唯一の方法です。",
"Credentials": "認証情報",
"Tokens, usernames, and passwords for OPDS, KOReader, Hardcover, and Readwise. When disabled, credentials remain on this device only and are never uploaded.": "OPDS、KOReader、Hardcover、Readwise のトークン、ユーザー名、パスワード。無効にすると、認証情報はこのデバイスにのみ残り、アップロードされません。",
"Sensitive synced fields are encrypted before upload. Set a passphrase now or later when encryption is needed.": "機密性の高い同期フィールドはアップロード前に暗号化されます。パスフレーズは今すぐ設定するか、暗号化が必要なときに後で設定できます。",
"Saved to this account. You will be prompted for it when decrypting credentials.": "このアカウントに保存されました。認証情報を復号する際に入力を求められます。",
"Reading progress on this device differs from \"{{deviceName}}\".": "このデバイスの読書進捗は「{{deviceName}}」と異なります。",
"This device": "このデバイス",
"Red": "赤",
"Yellow": "黄",
"Green": "緑",
"Blue": "青",
"Violet": "紫",
"Edit color": "色を編集",
"Add custom color": "カスタムカラーを追加",
"Add label": "ラベルを追加",
"Choose color": "色を選択",
"Connected to Hardcover. Last synced {{time}}.": "Hardcoverに接続済み。最終同期 {{time}}。",
"Integrations": "連携",
"Hardcover": "Hardcover",
"Connected to Readwise. Last synced {{time}}.": "Readwiseに接続済み。最終同期 {{time}}。",
"Readwise": "Readwise",
"Browse and download books from online catalogs.": "オンラインカタログから書籍を閲覧・ダウンロードします。",
"Connected as {{user}}": "{{user}} として接続済み",
"Not connected": "未接続",
"{{count}} catalog_other": "{{count}} カタログ",
"No catalogs": "カタログなし",
"Connect Readest to external services for sync, highlights, and catalogs.": "同期、ハイライト、カタログのために Readest を外部サービスに接続します。",
"Reading Sync": "読書同期",
"Content Sources": "コンテンツソース",
"Scroll tabs": "タブをスクロール",
"Catalog actions": "カタログ操作",
"File Content": "ファイル内容",
"Start your library": "ライブラリーを作成しましょう",
"Pick a book from your device to add it to your library.": "デバイスから本を選んでライブラリーに追加しましょう。",
"Drop a book anywhere on this window, or pick one from your computer.": "このウィンドウの好きな場所に本をドロップするか、コンピューターから選んでください。",
"Sign in to sync your library": "サインインしてライブラリーを同期"
}
@@ -102,11 +102,9 @@
"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": "이 책에 적용",
@@ -173,9 +171,6 @@
"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": "커뮤니티 지원",
@@ -188,7 +183,6 @@
"RTL Direction": "RTL 방향",
"Maximum Column Height": "최대 열 높이",
"Maximum Column Width": "최대 열 너비",
"Continuous Scroll": "연속 스크롤",
"Fullscreen": "전체 화면",
"No supported files found. Supported formats: {{formats}}": "지원되는 파일이 없습니다. 지원되는 형식: {{formats}}",
"Drop to Import Books": "책 가져오기 위해 드롭",
@@ -302,7 +296,6 @@
"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}}분 남음",
@@ -335,10 +328,10 @@
"Fit": "맞춤",
"Reset {{settings}}": "{{settings}} 재설정",
"Reset Settings": "설정 재설정",
"{{count}} pages left in chapter_other": "남은 페이지 {{count}}",
"{{count}} pages left in chapter_other": "<1>남은 페이지 </1><0>{{count}}</0><1>장</1>",
"Show Remaining Pages": "남은 페이지 보기",
"Source Han Serif CN VF": "Source Han Serif",
"Huiwen-mincho": "Huiwen Mincho",
"Source Han Serif CN": "Source Han Serif",
"Huiwen-MinchoGBK": "Huiwen Mincho",
"KingHwa_OldSong": "KingHwa Song",
"Manage Subscription": "구독 관리",
"Coming Soon": "곧 출시 예정",
@@ -462,14 +455,12 @@
"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": "변경 사항만 수신",
"Checksum Method": "체크섬 방법",
"File Content (recommended)": "파일 내용 (권장)",
"File Name": "파일 이름",
"Device Name": "장치 이름",
"Connect to your KOReader Sync server.": "KOReader Sync 서버에 연결합니다.",
@@ -480,10 +471,7 @@
"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}}%)",
@@ -598,11 +586,10 @@
"Size": "크기",
"Cover": "표지",
"Contain": "포함",
"{{number}} pages left in chapter": "남은 페이지 {{number}}",
"{{number}} pages left in chapter": "<1>남은 페이지 </1><0>{{number}}</0><1>장</1>",
"Device": "장치",
"E-Ink Mode": "E-Ink 모드",
"Highlight Colors": "하이라이트 색상",
"Auto Screen Brightness": "자동 화면 밝기",
"Pagination": "페이지 매김",
"Disable Double Tap": "더블 탭 비활성화",
"Tap to Paginate": "탭하여 페이지 매김",
@@ -637,7 +624,6 @@
"Strikethrough": "취소선",
"Squiggly": "물결선",
"Outline": "윤곽선",
"Save Current Color": "현재 색상 저장",
"Quick Colors": "빠른 색상",
"Highlighter": "형광펜",
"Save Book Cover": "책 표지 저장",
@@ -695,9 +681,7 @@
"Validating...": "검증 중...",
"View All": "모두 보기",
"Forward": "다음",
"OPDS Catalog": "OPDS 카탈로그",
"Home": "홈",
"Library": "라이브러리",
"{{count}} items_other": "{{count}}개 항목",
"Download completed": "다운로드 완료",
"Download failed": "다운로드 실패",
@@ -716,5 +700,692 @@
"Last": "마지막",
"Cannot Load Page": "페이지를 불러올 수 없습니다",
"An error occurred": "오류가 발생했습니다",
"Online Library": "온라인 라이브러리"
"Online Library": "온라인 라이브러리",
"URL must start with http:// or https://": "URL은 http:// 또는 https://로 시작해야 합니다",
"Title, Author, Tag, etc...": "제목, 저자, 태그 등...",
"Query": "쿼리",
"Subject": "주제",
"Enter {{terms}}": "{{terms}} 입력",
"No search results found": "검색 결과가 없습니다",
"Failed to load OPDS feed: {{status}} {{statusText}}": "OPDS 피드를 불러오지 못했습니다: {{status}} {{statusText}}",
"Search in {{title}}": "{{title}}에서 검색",
"Manage Storage": "저장소 관리",
"Failed to load files": "파일 로드 실패",
"Deleted {{count}} file(s)_other": "{{count}}개의 파일이 삭제되었습니다",
"Failed to delete {{count}} file(s)_other": "{{count}}개의 파일 삭제 실패",
"Failed to delete files": "파일 삭제 실패",
"Total Files": "총 파일",
"Total Size": "총 용량",
"Quota": "쿼터",
"Used": "사용됨",
"Files": "파일",
"Search files...": "파일 검색...",
"Newest First": "최신순",
"Oldest First": "오래된순",
"Largest First": "큰 파일순",
"Smallest First": "작은 파일순",
"Name A-Z": "이름 A-Z",
"Name Z-A": "이름 Z-A",
"{{count}} selected_other": "{{count}}개 선택됨",
"Delete Selected": "선택 삭제",
"Created": "생성됨",
"No files found": "파일이 없습니다",
"No files uploaded yet": "아직 업로드된 파일이 없습니다",
"files": "파일",
"Page {{current}} of {{total}}": "페이지 {{current}} / {{total}}",
"Are you sure to delete {{count}} selected file(s)?_other": "선택한 {{count}}개의 파일을 삭제하시겠습니까?",
"Cloud Storage Usage": "클라우드 저장소 사용량",
"Rename Group": "그룹 이름 바꾸기",
"From Directory": "디렉토리에서",
"Successfully imported {{count}} book(s)_other": "성공적으로 {{count}} 권의 책이 가져와졌습니다",
"Count": "개수",
"Start Page": "시작 페이지",
"Search in OPDS Catalog...": "OPDS 카탈로그에서 검색...",
"Please log in to use advanced TTS features": "고급 TTS 기능을 사용하려면 로그인하세요",
"Word limit of 30 words exceeded.": "30단어 제한을 초과했습니다.",
"Proofread": "교정",
"Current selection": "현재 선택",
"All occurrences in this book": "이 책의 모든 위치",
"All occurrences in your library": "라이브러리의 모든 위치",
"Selected text:": "선택한 텍스트:",
"Replace with:": "다음으로 바꾸기:",
"Enter text...": "텍스트 입력…",
"Case sensitive:": "대소문자 구분:",
"Scope:": "적용 범위:",
"Selection": "선택",
"Library": "라이브러리",
"Yes": "예",
"No": "아니요",
"Proofread Replacement Rules": "교정 대체 규칙",
"Selected Text Rules": "선택한 텍스트 규칙",
"No selected text replacement rules": "선택한 텍스트에 대한 대체 규칙이 없습니다",
"Book Specific Rules": "책별 규칙",
"No book-level replacement rules": "책 수준의 대체 규칙이 없습니다",
"Disable Quick Action": "빠른 작업 비활성화",
"Enable Quick Action on Selection": "선택 시 빠른 작업 활성화",
"None": "없음",
"Annotation Tools": "주석 도구",
"Enable Quick Actions": "빠른 작업 활성화",
"Quick Action": "빠른 작업",
"Copy to Notebook": "노트에 복사",
"Copy text after selection": "선택 후 텍스트 복사",
"Highlight text after selection": "선택 후 텍스트 강조",
"Annotate text after selection": "선택 후 텍스트 주석 달기",
"Search text after selection": "선택 후 텍스트 검색",
"Look up text in dictionary after selection": "선택 후 텍스트를 사전에서 찾기",
"Translate text after selection": "선택 후 텍스트 번역",
"Read text aloud after selection": "선택 후 텍스트 음성 읽기",
"Proofread text after selection": "선택 후 텍스트 교정",
"{{activeCount}} active, {{pendingCount}} pending": "{{activeCount}} 진행 중, {{pendingCount}} 대기 중",
"{{failedCount}} failed": "{{failedCount}} 실패",
"Waiting...": "대기 중...",
"Failed": "실패",
"Completed": "완료됨",
"Cancelled": "취소됨",
"Retry": "재시도",
"Active": "진행 중",
"Transfer Queue": "전송 대기열",
"Upload All": "모두 업로드",
"Download All": "모두 다운로드",
"Resume Transfers": "전송 재개",
"Pause Transfers": "전송 일시정지",
"Pending": "대기 중",
"No transfers": "전송 없음",
"Retry All": "모두 재시도",
"Clear Completed": "완료된 항목 지우기",
"Clear Failed": "실패한 항목 지우기",
"Upload queued: {{title}}": "업로드 대기열에 추가됨: {{title}}",
"Download queued: {{title}}": "다운로드 대기열에 추가됨: {{title}}",
"Book not found in library": "라이브러리에서 책을 찾을 수 없음",
"Unknown error": "알 수 없는 오류",
"Please log in to continue": "계속하려면 로그인하세요",
"Cloud File Transfers": "클라우드 파일 전송",
"Show Search Results": "검색 결과 보기",
"Search results for '{{term}}'": "'{{term}}' 검색 결과",
"Close Search": "검색 닫기",
"Previous Result": "이전 결과",
"Next Result": "다음 결과",
"Bookmarks": "북마크",
"Annotations": "주석",
"Show Results": "결과 표시",
"Clear search": "검색 지우기",
"Clear search history": "검색 기록 지우기",
"Tap to Toggle Footer": "탭하여 바닥글 전환",
"Show Current Time": "현재 시간 표시",
"Use 24 Hour Clock": "24시간제 사용",
"Show Current Battery Status": "현재 배터리 상태 표시",
"Exported successfully": "내보내기 성공",
"Book exported successfully.": "책을 성공적으로 내보냈습니다.",
"Failed to export the book.": "책 내보내기에 실패했습니다.",
"Export Book": "책 내보내기",
"Whole word:": "전체 단어:",
"Date Published": "출판일",
"Only for TTS:": "TTS 전용:",
"Uploaded": "업로드됨",
"Downloaded": "다운로드됨",
"Deleted": "삭제됨",
"Note:": "노트:",
"Time:": "시간:",
"Format Options": "형식 옵션",
"Export Date": "내보내기 날짜",
"Chapter Titles": "챕터 제목",
"Chapter Separator": "챕터 구분 기호",
"Highlights": "하이라이트",
"Note Date": "노트 날짜",
"Advanced": "고급",
"Hide": "숨기기",
"Show": "표시",
"Use Custom Template": "사용자 지정 템플릿 사용",
"Export Template": "내보내기 템플릿",
"Template Syntax:": "템플릿 구문:",
"Insert value": "값 삽입",
"Format date (locale)": "날짜 형식 (로케일)",
"Format date (custom)": "날짜 형식 (사용자 지정)",
"Conditional": "조건부",
"Loop": "반복",
"Available Variables:": "사용 가능한 변수:",
"Book title": "책 제목",
"Book author": "책 저자",
"Export date": "내보내기 날짜",
"Array of chapters": "챕터 배열",
"Chapter title": "챕터 제목",
"Array of annotations": "주석 배열",
"Highlighted text": "하이라이트된 텍스트",
"Annotation note": "주석 노트",
"Date Format Tokens:": "날짜 형식 토큰:",
"Year (4 digits)": "연도 (4자리)",
"Month (01-12)": "월 (01-12)",
"Day (01-31)": "일 (01-31)",
"Hour (00-23)": "시 (00-23)",
"Minute (00-59)": "분 (00-59)",
"Second (00-59)": "초 (00-59)",
"Show Source": "소스 표시",
"No content to preview": "미리 볼 내용 없음",
"Export": "내보내기",
"Set Timeout": "시간 제한 설정",
"Select Voice": "음성 선택",
"Toggle Sticky Bottom TTS Bar": "고정 TTS 바 전환",
"Display what I'm reading on Discord": "Discord에 읽는 책 표시",
"Show on Discord": "Discord에 표시",
"Instant {{action}}": "즉시 {{action}}",
"Instant {{action}} Disabled": "즉시 {{action}} 비활성화",
"Annotation": "주석",
"Reset Template": "템플릿 초기화",
"Annotation style": "주석 스타일",
"Annotation color": "주석 색상",
"Annotation time": "주석 시간",
"AI": "AI",
"Are you sure you want to re-index this book?": "이 책을 다시 인덱싱하시겠습니까?",
"Enable AI in Settings": "설정에서 AI 활성화",
"Index This Book": "이 책 인덱싱",
"Enable AI search and chat for this book": "이 책에 대한 AI 검색 및 채팅 활성화",
"Start Indexing": "인덱싱 시작",
"Indexing book...": "책 인덱싱 중...",
"Preparing...": "준비 중...",
"Delete this conversation?": "이 대화를 삭제하시겠습니까?",
"No conversations yet": "아직 대화가 없습니다",
"Start a new chat to ask questions about this book": "이 책에 대해 질문하려면 새 채팅을 시작하세요",
"Rename": "이름 변경",
"New Chat": "새 채팅",
"Chat": "채팅",
"Please enter a model ID": "모델 ID를 입력하세요",
"Model not available or invalid": "모델을 사용할 수 없거나 유효하지 않습니다",
"Failed to validate model": "모델 검증 실패",
"Couldn't connect to Ollama. Is it running?": "Ollama에 연결할 수 없습니다. 실행 중인가요?",
"Invalid API key or connection failed": "API 키가 유효하지 않거나 연결 실패",
"Connection failed": "연결 실패",
"AI Assistant": "AI 어시스턴트",
"Enable AI Assistant": "AI 어시스턴트 활성화",
"Provider": "제공자",
"Ollama (Local)": "Ollama (로컬)",
"AI Gateway (Cloud)": "AI 게이트웨이 (클라우드)",
"Ollama Configuration": "Ollama 설정",
"Refresh Models": "모델 새로고침",
"AI Model": "AI 모델",
"No models detected": "모델이 감지되지 않았습니다",
"AI Gateway Configuration": "AI 게이트웨이 설정",
"Choose from a selection of high-quality, economical AI models. You can also bring your own model by selecting \"Custom Model\" below.": "고품질의 경제적인 AI 모델 중에서 선택하세요. 아래의 \"사용자 정의 모델\"을 선택하여 자신의 모델을 사용할 수도 있습니다.",
"API Key": "API 키",
"Get Key": "키 받기",
"Model": "모델",
"Custom Model...": "사용자 정의 모델...",
"Custom Model ID": "사용자 정의 모델 ID",
"Validate": "검증",
"Model available": "모델 사용 가능",
"Connection": "연결",
"Test Connection": "연결 테스트",
"Connected": "연결됨",
"Custom Colors": "사용자 정의 색상",
"Color E-Ink Mode": "컬러 E-Ink 모드",
"Reading Ruler": "읽기 자",
"Enable Reading Ruler": "읽기 자 활성화",
"Lines to Highlight": "강조할 줄 수",
"Ruler Color": "자 색상",
"Command Palette": "명령 팔레트",
"Search settings and actions...": "설정 및 작업 검색...",
"No results found for": "에 대한 결과를 찾을 수 없음",
"Type to search settings and actions": "입력하여 설정 및 작업 검색",
"Recent": "최근",
"navigate": "탐색",
"select": "선택",
"close": "닫기",
"Search Settings": "설정 검색",
"Page Margins": "페이지 여백",
"AI Provider": "AI 제공처",
"Ollama URL": "Ollama URL",
"Ollama Model": "Ollama 모델",
"AI Gateway Model": "AI 게이트웨이 모델",
"Actions": "작업",
"Navigation": "탐색",
"Set status for {{count}} book(s)_other": "책 {{count}}권 상태 설정",
"Mark as Unread": "읽지 않음으로 표시",
"Mark as Finished": "완료로 표시",
"Finished": "완료",
"Unread": "읽지 않음",
"Clear Status": "상태 지우기",
"Status": "상태",
"Loading": "로드 중...",
"Exit Paragraph Mode": "단락 모드 종료",
"Paragraph Mode": "단락 모드",
"Embedding Model": "임베딩 모델",
"{{count}} book(s) synced_other": "{{count}}권의 책이 동기화되었습니다",
"Unable to start RSVP": "RSVP를 시작할 수 없습니다",
"RSVP not supported for PDF": "PDF는 RSVP를 지원하지 않습니다",
"Select Chapter": "챕터 선택",
"Context": "문맥",
"Ready": "준비됨",
"Chapter Progress": "챕터 진행 상황",
"words": "단어",
"{{time}} left": "{{time}} 남음",
"Reading progress": "독서 진행 상황",
"Skip back 15 words": "15단어 뒤로",
"Back 15 words (Shift+Left)": "15단어 뒤로 (Shift+왼쪽 화살표)",
"Pause (Space)": "일시정지 (스페이스바)",
"Play (Space)": "재생 (스페이스바)",
"Skip forward 15 words": "15단어 앞으로",
"Forward 15 words (Shift+Right)": "15단어 앞으로 (Shift+오른쪽 화살표)",
"Decrease speed": "속도 줄이기",
"Slower (Left/Down)": "느리게 (왼쪽/아래 화살표)",
"Increase speed": "속도 높이기",
"Faster (Right/Up)": "빠르게 (오른쪽/위 화살표)",
"Start RSVP Reading": "RSVP 독서 시작",
"Choose where to start reading": "독서를 시작할 위치 선택",
"From Chapter Start": "챕터 시작부터",
"Start reading from the beginning of the chapter": "챕터 처음부터 다시 시작",
"Resume": "재개",
"Continue from where you left off": "중단한 지점부터 계속 읽기",
"From Current Page": "현재 페이지부터",
"Start from where you are currently reading": "현재 읽고 있는 위치부터 시작",
"From Selection": "선택 범위부터",
"Speed Reading Mode": "속독 모드",
"Scroll left": "왼쪽으로 스크롤",
"Scroll right": "오른쪽으로 스크롤",
"Library Sync Progress": "라이브러리 동기화 진행 상황",
"Back to library": "라이브러리로 돌아가기",
"Group by...": "그룹화 기준...",
"Export as Plain Text": "일반 텍스트로 내보내기",
"Export as Markdown": "Markdown으로 내보내기",
"Show Page Navigation Buttons": "탐색 버튼 표시",
"Page {{number}}": "{{number}} 페이지",
"highlight": "하이라이트",
"underline": "밑줄",
"squiggly": "구불구불한 선",
"red": "빨간색",
"violet": "보라색",
"blue": "파란색",
"green": "초록색",
"yellow": "노란색",
"Select {{style}} style": "{{style}} 스타일 선택",
"Select {{color}} color": "{{color}} 색상 선택",
"Close Book": "책 닫기",
"Speed Reading": "속독",
"Close Speed Reading": "속독 닫기",
"Authors": "저자",
"Books": "도서",
"Groups": "그룹",
"Back to TTS Location": "TTS 위치로 돌아가기",
"Metadata": "메타데이터",
"Image viewer": "이미지 뷰어",
"Previous Image": "이전 이미지",
"Next Image": "다음 이미지",
"Zoomed": "확대됨",
"Zoom level": "확대 수준",
"Table viewer": "테이블 뷰어",
"Unable to connect to Readwise. Please check your network connection.": "Readwise에 연결할 수 없습니다. 네트워크 연결을 확인해 주세요.",
"Invalid Readwise access token": "유효하지 않은 Readwise 액세스 토큰입니다",
"Disconnected from Readwise": "Readwise와 연결이 끊어졌습니다",
"Never": "안 함",
"Sync Enabled": "동기화 활성화됨",
"Disconnect": "연결 해제",
"Connect your Readwise account to sync highlights.": "하이라이트를 동기화하려면 Readwise 계정렬 연결하세요.",
"Get your access token at": "액세스 토큰 받기:",
"Access Token": "액세스 토큰",
"Paste your Readwise access token": "Readwise 액세스 토큰을 붙여넣으세요",
"Readwise Sync": "Readwise 동기화",
"Push Highlights": "하이라이트 푸시",
"Highlights synced to Readwise": "하이라이트가 Readwise에 동기화되었습니다",
"Readwise sync failed: no internet connection": "Readwise 동기화 실패: 인터넷 연결 없음",
"Readwise sync failed: {{error}}": "Readwise 동기화 실패: {{error}}",
"System Screen Brightness": "시스템 화면 밝기",
"Page:": "페이지:",
"Page: {{number}}": "페이지: {{number}}",
"Annotation page number": "주석 페이지 번호",
"Translating...": "번역 중...",
"Show Battery Percentage": "배터리 백분율 표시",
"Hide Scrollbar": "스크롤바 숨기기",
"Skip to last reading position": "마지막 읽기 위치로 이동",
"Show context": "맥락 표시",
"Hide context": "맥락 숨기기",
"Decrease font size": "글꼴 크기 줄이기",
"Increase font size": "글꼴 크기 늘리기",
"Focus": "초점",
"Theme color": "테마 색상",
"Import failed": "가져오기 실패",
"Available Formatters:": "사용 가능한 포맷터:",
"Format date": "날짜 형식",
"Markdown block quote (> per line)": "Markdown 인용 블록 (줄마다 >)",
"Newlines to <br>": "줄바꿈을 <br>로 변환",
"Change case": "대소문자 변경",
"Trim whitespace": "공백 제거",
"Truncate to n characters": "n자로 자르기",
"Replace text": "텍스트 바꾸기",
"Fallback value": "기본값",
"Get length": "길이 가져오기",
"First/last element": "첫 번째/마지막 요소",
"Join array": "배열 합치기",
"Backup failed: {{error}}": "백업 실패: {{error}}",
"Select Backup": "백업 선택",
"Restore failed: {{error}}": "복원 실패: {{error}}",
"Backup & Restore": "백업 및 복원",
"Create a backup of your library or restore from a previous backup. Restoring will merge with your current library.": "라이브러리를 백업하거나 이전 백업에서 복원하세요. 복원 시 현재 라이브러리와 병합됩니다.",
"Backup Library": "라이브러리 백업",
"Restore Library": "라이브러리 복원",
"Creating backup...": "백업 생성 중...",
"Restoring library...": "라이브러리 복원 중...",
"{{current}} of {{total}} items": "{{current}} / {{total}} 항목",
"Backup completed successfully!": "백업이 완료되었습니다!",
"Restore completed successfully!": "복원이 완료되었습니다!",
"Your library has been saved to the selected location.": "라이브러리가 선택한 위치에 저장되었습니다.",
"{{added}} books added, {{updated}} books updated.": "{{added}}권 추가, {{updated}}권 업데이트되었습니다.",
"Operation failed": "작업 실패",
"Loading library...": "라이브러리 로딩 중...",
"{{count}} books refreshed_other": "{{count}}권 새로고침됨",
"Failed to refresh metadata": "메타데이터 새로고침 실패",
"Refresh Metadata": "메타데이터 새로고침",
"Keyboard Shortcuts": "키보드 단축키",
"View all keyboard shortcuts": "모든 키보드 단축키 보기",
"Switch Sidebar Tab": "사이드바 탭 전환",
"Toggle Notebook": "노트북 표시/숨기기",
"Search in Book": "책에서 검색",
"Toggle Scroll Mode": "스크롤 모드 전환",
"Toggle Select Mode": "선택 모드 전환",
"Toggle Bookmark": "북마크 전환",
"Toggle Text to Speech": "텍스트 음성 변환 전환",
"Play / Pause TTS": "TTS 재생 / 일시정지",
"Toggle Paragraph Mode": "문단 모드 전환",
"Highlight Selection": "선택 영역 강조",
"Underline Selection": "선택 영역 밑줄",
"Annotate Selection": "선택 영역 주석",
"Search Selection": "선택 영역 검색",
"Copy Selection": "선택 영역 복사",
"Translate Selection": "선택 영역 번역",
"Dictionary Lookup": "사전 검색",
"Read Aloud Selection": "선택 영역 소리 내어 읽기",
"Proofread Selection": "선택 영역 교정",
"Open Settings": "설정 열기",
"Open Command Palette": "명령 팔레트 열기",
"Show Keyboard Shortcuts": "키보드 단축키 표시",
"Open Books": "책 열기",
"Toggle Fullscreen": "전체 화면 전환",
"Close Window": "창 닫기",
"Quit App": "앱 종료",
"Go Left / Previous Page": "왼쪽으로 / 이전 페이지",
"Go Right / Next Page": "오른쪽으로 / 다음 페이지",
"Go Up": "위로",
"Go Down": "아래로",
"Previous Chapter": "이전 장",
"Next Chapter": "다음 장",
"Scroll Half Page Down": "반 페이지 아래로 스크롤",
"Scroll Half Page Up": "반 페이지 위로 스크롤",
"Save Note": "메모 저장",
"Single Section Scroll": "단일 섹션 스크롤",
"General": "일반",
"Text to Speech": "텍스트 음성 변환",
"Zoom": "확대/축소",
"Window": "창",
"Your Bookshelf": "내 책장",
"TTS": "음성 읽기",
"Media Info": "미디어 정보",
"Update Frequency": "업데이트 빈도",
"Every Sentence": "문장마다",
"Every Paragraph": "단락마다",
"Every Chapter": "챕터마다",
"TTS Media Info Update Frequency": "TTS 미디어 정보 업데이트 빈도",
"Select reading speed": "읽기 속도 선택",
"Drag to seek": "드래그하여 탐색",
"Punctuation Delay": "구두점 지연",
"Please confirm that this OPDS connection will be proxied through Readest servers on the web app before continuing.": "계속하기 전에 이 OPDS 연결이 웹 앱의 Readest 서버를 통해 프록시될 것임을 확인해 주세요.",
"Custom Headers (optional)": "사용자 정의 헤더 (선택 사항)",
"Add one header per line using \"Header-Name: value\".": "\"Header-Name: value\" 형식으로 한 줄에 하나의 헤더를 추가하세요.",
"I understand this OPDS connection will be proxied through Readest servers on the web app. If I do not trust Readest with these credentials or headers, I should use the native app instead.": "이 OPDS 연결이 웹 앱의 Readest 서버를 통해 프록시될 것임을 이해합니다. 이 자격 증명이나 헤더에 대해 Readest를 신뢰하지 않는다면 네이티브 앱을 사용해야 합니다.",
"Unable to connect to Hardcover. Please check your network connection.": "Hardcover에 연결할 수 없습니다. 네트워크 연결을 확인해 주세요.",
"Invalid Hardcover API token": "유효하지 않은 Hardcover API 토큰",
"Disconnected from Hardcover": "Hardcover에서 연결 해제됨",
"Connect your Hardcover account to sync reading progress and notes.": "독서 진행 상황과 메모를 동기화하려면 Hardcover 계정을 연결하세요.",
"Get your API token from hardcover.app → Settings → API.": "hardcover.app → 설정 → API에서 API 토큰을 받으세요.",
"API Token": "API 토큰",
"Paste your Hardcover API token": "Hardcover API 토큰을 붙여넣기",
"Hardcover Sync": "Hardcover 동기화",
"Push Notes": "메모 보내기",
"No annotations or excerpts to sync for this book.": "이 책에 동기화할 주석이나 발췌가 없습니다.",
"Configure Hardcover in Settings first.": "먼저 설정에서 Hardcover를 구성하세요.",
"No new Hardcover note changes to sync.": "동기화할 새로운 Hardcover 메모 변경 사항이 없습니다.",
"Hardcover synced: {{inserted}} new, {{updated}} updated, {{skipped}} unchanged": "Hardcover 동기화 완료: {{inserted}}개 추가, {{updated}}개 업데이트, {{skipped}}개 변경 없음",
"Hardcover notes sync failed: {{error}}": "Hardcover 메모 동기화 실패: {{error}}",
"Reading progress synced to Hardcover": "독서 진행 상황이 Hardcover에 동기화됨",
"Hardcover progress sync failed: {{error}}": "Hardcover 진행 상황 동기화 실패: {{error}}",
"ISBN": "ISBN",
"Keep existing source identifier": "기존 소스 식별자 유지",
"Toggle Toolbar": "도구 모음 전환",
"Split Hyphens": "하이픈 분리",
"Apply Theme Colors to PDF": "PDF에 테마 색상 적용",
"Name": "이름",
"Remove": "삭제",
"Edit OPDS Catalog": "OPDS 카탈로그 편집",
"Save Changes": "변경 사항 저장",
"Use Book Layout": "책 레이아웃 사용",
"End of this section. Continue to the next.": "이 섹션의 끝입니다. 다음으로 계속하세요.",
"Auto-download": "자동 다운로드",
"Auto-download new items": "새 항목 자동 다운로드",
"Automatically download new publications when the app syncs": "앱 동기화 시 새 출판물 자동 다운로드",
"{{count}} new item(s) downloaded from OPDS_other": "OPDS에서 {{count}}개의 새 항목이 다운로드됨",
"Failed to sync {{count}} OPDS catalog(s)_other": "OPDS 카탈로그 {{count}}개 동기화 실패",
"Last synced {{when}}": "마지막 동기화 {{when}}",
"Failed downloads": "실패한 다운로드",
"No failed downloads": "실패한 다운로드 없음",
"Attempts: {{count}}_other": "시도: {{count}}",
"Skip": "건너뛰기",
"Skip all": "모두 건너뛰기",
"Retry all": "모두 재시도",
"{{count}} failed_other": "{{count}}개 실패",
"(none)": "(없음)",
"Identifiers": "식별자",
"Read (Stream)": "읽기 (스트림)",
"Failed to start stream": "스트림을 시작할 수 없습니다",
"No dictionaries enabled": "활성화된 사전이 없습니다",
"Enable a dictionary in Settings → Language → Dictionaries.": "설정 → 언어 → 사전 에서 사전을 활성화하세요.",
"Wiktionary": "위키낱말사전",
"Drag to reorder": "드래그하여 순서 변경",
"Built-in": "내장",
"Bundle is missing on this device. Re-import to use it.": "이 기기에 번들이 없습니다. 사용하려면 다시 가져오세요.",
"This dictionary format is not supported.": "이 사전 형식은 지원되지 않습니다.",
"MDict": "MDict",
"StarDict": "StarDict",
"Imported {{count}} dictionary_other": "{{count}}개 사전 가져옴",
"Skipped incomplete bundles: {{names}}": "불완전한 번들 건너뜀: {{names}}",
"Failed to import dictionary: {{message}}": "사전 가져오기 실패: {{message}}",
"Dictionaries": "사전",
"Delete Dictionary": "사전 삭제",
"Importing…": "가져오는 중…",
"Import Dictionary": "사전 가져오기",
"No dictionaries available.": "사용 가능한 사전이 없습니다.",
"StarDict bundles need .ifo, .idx, and .dict.dz files (.syn optional).": "StarDict 번들은 .ifo, .idx, .dict.dz 파일이 필요합니다(.syn은 선택).",
"Select all the bundle files together when importing.": "가져올 때 번들의 모든 파일을 함께 선택하세요.",
"Manage Dictionaries": "사전 관리",
"Select Dictionary Files": "사전 파일 선택",
"Read on Wikipedia →": "위키백과에서 읽기 →",
"This link can't be opened": "이 링크를 열 수 없습니다",
"The annotation link is missing required information. The original link may have been truncated.": "주석 링크에 필요한 정보가 누락되었습니다. 원본 링크가 잘렸을 수 있습니다.",
"Go to Readest": "Readest로 이동",
"Open-source ebook reader for everyone, on every device.": "누구나, 어떤 기기에서나 사용할 수 있는 오픈소스 전자책 리더.",
"Open in Readest": "Readest에서 열기",
"If Readest didn't open automatically, choose an option below:": "Readest가 자동으로 열리지 않았다면 아래에서 선택하세요:",
"Continue reading where you left off.": "마지막으로 읽던 곳부터 이어서 읽으세요.",
"Readest logo": "Readest 로고",
"Opening Readest...": "Readest를 여는 중…",
"Open in Readest app": "Readest 앱에서 열기",
"Continue in browser": "브라우저에서 계속",
"Don't have Readest?": "Readest가 없으신가요?",
"Book not in your library": "책이 라이브러리에 없습니다",
"Share Book": "책 공유",
"Could not create share link": "공유 링크를 만들 수 없습니다",
"Link copied": "링크가 복사되었습니다",
"Could not copy link": "링크를 복사할 수 없습니다",
"Share revoked": "공유가 취소되었습니다",
"Could not revoke share": "공유를 취소할 수 없습니다",
"Uploading book…": "책 업로드 중…",
"Generating…": "생성 중…",
"Generate share link": "공유 링크 만들기",
"Share URL": "공유 URL",
"Copy link": "링크 복사",
"Copied": "복사됨",
"Share via…": "공유…",
"Expires {{date}}": "{{date}}에 만료",
"Revoking…": "취소 중…",
"Revoke share": "공유 취소",
"Sign in to share books": "책을 공유하려면 로그인하세요",
"Expiring soon": "곧 만료",
"Missing share token": "공유 토큰이 없습니다",
"Could not add to your library": "내 라이브러리에 추가할 수 없습니다",
"This share link is no longer available": "이 공유 링크는 더 이상 사용할 수 없습니다",
"The original link may have expired or been revoked.": "원래 링크가 만료되었거나 취소되었을 수 있습니다.",
"Get Readest": "Readest 받기",
"Loading shared book…": "공유된 책을 불러오는 중…",
"Adding…": "추가 중…",
"Add to my library": "내 라이브러리에 추가",
"Could not load your shares": "공유를 불러올 수 없습니다",
"Revoked": "취소됨",
"Expired": "만료됨",
"Shared books": "공유된 책",
"You haven't shared any books yet": "아직 책을 공유하지 않았습니다",
"Open a book and tap Share to send it to a friend.": "책을 열고 공유를 눌러 친구에게 보내세요.",
"{{count}} active_other": "활성 {{count}}개",
"{{count}} downloads_other": "{{count}}회 다운로드",
"starts at saved page": "저장된 페이지에서 시작",
"More actions": "추가 작업",
"Loading…": "불러오는 중…",
"Load more": "더 보기",
"Could not load shared book": "공유된 책을 불러올 수 없습니다",
"The share link is missing required information.": "공유 링크에 필요한 정보가 없습니다.",
"Please check your connection and try again.": "연결을 확인한 후 다시 시도하세요.",
"Sign in to import shared books": "공유된 책을 가져오려면 로그인하세요",
"Already in your library": "이미 라이브러리에 있습니다",
"Added to your library": "라이브러리에 추가되었습니다",
"Could not import shared book": "공유된 책을 가져올 수 없습니다",
"Manage Shared Links": "공유 링크 관리",
"Expires in": "만료",
"{{count}} days_other": "{{count}}일",
"Expires in {{count}} days_other": "{{count}}일 후 만료",
"Expires in {{count}} hours_other": "{{count}}시간 후 만료",
"Open in app": "앱에서 열기",
"Shared with you": "회원님과 공유됨",
"Downloading… {{percent}}%": "다운로드 중… {{percent}}%",
"Import progress": "가져오기 진행률",
"Share reading progress": "읽기 진행률 공유",
"Includes your reading progress": "읽기 진행률 포함됨",
"Synced {{time}}": "{{time}} 동기화됨",
"Sync Info": "동기화 정보",
"Last Synced": "마지막 동기화",
"DICT": "DICT",
"Slob": "Slob",
"DICT bundles need a .index file and a .dict.dz file.": "DICT 번들에는 .index 파일과 .dict.dz 파일이 필요합니다.",
"Slob bundles need a .slob file.": "Slob 번들에는 .slob 파일이 필요합니다.",
"URL template must start with http(s):// and contain %WORD%.": "URL 템플릿은 http(s):// 로 시작하고 %WORD%를 포함해야 합니다.",
"Web": "웹",
"Add Web Search": "웹 검색 추가",
"Edit Web Search": "웹 검색 편집",
"e.g. Google": "예: Google",
"URL Template": "URL 템플릿",
"Use %WORD% where the looked-up word should appear.": "검색어가 표시될 위치에 %WORD%를 사용하세요.",
"Open the search result in your browser:": "브라우저에서 검색 결과 열기:",
"Open in {{name}}": "{{name}}에서 열기",
"Book Fingerprint": "책 지문",
"Search the web": "웹에서 검색",
"App deeplink (readest://)": "앱 딥링크 (readest://)",
"Universal web link (https://)": "범용 웹 링크 (https://)",
"Name cannot be empty.": "이름은 비워 둘 수 없습니다.",
"Replaced {{count}} existing dictionary_other": "기존 사전 {{count}}개를 교체했습니다",
"Cancel Edit": "편집 취소",
"Edit Dictionary": "사전 편집",
"MDict bundles use .mdx files; companion .mdd and .css files are optional.": "MDict 번들은 .mdx 파일을 사용합니다. 동반된 .mdd 및 .css 파일은 선택 사항입니다.",
"Dictionary name": "사전 이름",
"This audio can't play here — the dictionary uses an outdated format. Try one with Opus, MP3, or WAV audio.": "여기서는 이 오디오를 재생할 수 없습니다 — 사전이 오래된 형식을 사용합니다. Opus, MP3 또는 WAV 오디오 사전을 사용해 보세요.",
"File uploaded: {{title}}": "파일 업로드됨: {{title}}",
"File downloaded: {{title}}": "파일 다운로드됨: {{title}}",
"Deleted cloud copy of the file: {{title}}": "파일의 클라우드 사본이 삭제됨: {{title}}",
"Failed to upload file: {{title}}": "파일 업로드 실패: {{title}}",
"Failed to download file: {{title}}": "파일 다운로드 실패: {{title}}",
"Failed to delete cloud copy of the file: {{title}}": "파일의 클라우드 사본 삭제에 실패했습니다: {{title}}",
"{{percentage}}% used": "{{percentage}}% 사용됨",
"Set PIN…": "PIN 설정…",
"Require a 4-digit PIN to open Readest": "Readest를 열 때 4자리 PIN 요구",
"Change PIN…": "PIN 변경…",
"Disable PIN…": "PIN 비활성화…",
"Sync passphrase ready": "동기화 암호 문구가 준비되었습니다",
"This permanently deletes the encrypted credentials we sync (e.g., OPDS catalog passwords) on every device. Local copies are preserved. You will need to re-enter the sync passphrase or set a new one. Continue?": "이 작업을 수행하면 모든 기기에서 동기화하는 암호화된 자격 증명(예: OPDS 카탈로그 비밀번호)이 영구적으로 삭제됩니다. 로컬 복사본은 보존됩니다. 동기화 암호 문구를 다시 입력하거나 새로 설정해야 합니다. 계속하시겠습니까?",
"Sync passphrase forgotten — all encrypted fields cleared": "동기화 암호 문구를 잊었습니다 — 모든 암호화된 필드가 지워졌습니다",
"Sync passphrase": "동기화 암호 문구",
"Set passphrase": "암호 문구 설정",
"Forgot passphrase": "암호 문구를 잊으셨나요",
"Incorrect PIN": "잘못된 PIN",
"App locked": "앱이 잠겼습니다",
"Enter your PIN": "PIN을 입력하세요",
"Readest is locked. Enter your 4-digit PIN to continue.": "Readest가 잠겼습니다. 계속하려면 4자리 PIN을 입력하세요.",
"PIN code": "PIN 코드",
"Forgetting your PIN locks you out of this device. You'll need to clear the app's data to reset it.": "PIN을 잊으면 이 기기에서 잠금이 걸립니다. 재설정하려면 앱 데이터를 지워야 합니다.",
"Passphrase must be at least 8 characters": "암호 문구는 8자 이상이어야 합니다",
"Passphrases do not match": "암호 문구가 일치하지 않습니다",
"Set sync passphrase": "동기화 암호 문구 설정",
"Enter sync passphrase": "동기화 암호 문구 입력",
"A sync passphrase encrypts your sensitive fields (like OPDS catalog credentials) before they sync. We never see this passphrase. Pick something memorable — there is no recovery without it.": "동기화 암호 문구는 OPDS 카탈로그 자격 증명과 같은 민감한 필드를 동기화하기 전에 암호화합니다. 우리는 이 암호 문구를 결코 볼 수 없습니다. 기억하기 쉬운 것을 고르세요 — 잃어버리면 복구할 수 없습니다.",
"Enter the sync passphrase you set on another device to decrypt your synced credentials.": "다른 기기에서 설정한 동기화 암호 문구를 입력하여 동기화된 자격 증명을 복호화합니다.",
"Confirm passphrase": "암호 문구 확인",
"Unlock": "잠금 해제",
"Set PIN": "PIN 설정",
"Change PIN": "PIN 변경",
"Disable PIN": "PIN 비활성화",
"Enter your current PIN, then choose a new 4-digit PIN.": "현재 PIN을 입력한 다음 새 4자리 PIN을 선택하세요.",
"Enter your current PIN to disable the app lock.": "앱 잠금을 비활성화하려면 현재 PIN을 입력하세요.",
"PIN must be {{length}} digits": "PIN은 {{length}}자리여야 합니다",
"PINs do not match": "PIN이 일치하지 않습니다",
"Current PIN": "현재 PIN",
"New PIN": "새 PIN",
"Confirm new PIN": "새 PIN 확인",
"Manage Sync": "동기화 관리",
"Imported book files and library metadata": "가져온 도서 파일과 서재 메타데이터",
"Last-read position, bookmarks, and per-book preferences": "마지막 읽은 위치, 북마크, 도서별 환경설정",
"Highlights and notes": "하이라이트와 노트",
"Fonts": "글꼴",
"Custom font files": "사용자 지정 글꼴 파일",
"Backgrounds": "배경",
"Custom background textures": "사용자 지정 배경 텍스처",
"OPDS catalogs": "OPDS 카탈로그",
"Saved catalog URLs and (encrypted) credentials": "저장된 카탈로그 URL과 (암호화된) 자격 증명",
"Wrong sync passphrase — synced credentials could not be decrypted": "동기화 암호가 잘못되었습니다 — 동기화된 자격 증명을 복호화할 수 없습니다",
"Sync passphrase data on the server was reset. Re-encrypting your credentials under the new passphrase…": "서버의 동기화 암호 데이터가 재설정되었습니다. 새 암호로 자격 증명을 다시 암호화하는 중입니다…",
"Failed to decrypt synced credentials": "동기화된 자격 증명을 복호화하지 못했습니다",
"Imported dictionary bundles and settings": "가져온 사전 번들과 설정",
"Choose what syncs across your devices. Disabling a category stops this device from sending or receiving rows of that kind. Anything already on the server is left alone, re-enabling resumes from where you stopped.": "기기 간에 동기화할 항목을 선택하세요. 카테고리를 비활성화하면 이 기기는 해당 종류의 항목을 보내거나 받지 않습니다. 서버에 이미 있는 데이터는 그대로 유지되며, 다시 활성화하면 중단한 지점부터 이어집니다.",
"Data Sync": "데이터 동기화",
"Manage Fonts": "글꼴 관리",
"App settings": "앱 설정",
"Theme, highlight colours, integrations (KOSync, Readwise, Hardcover), and dictionary order": "테마, 하이라이트 색상, 연동 (KOSync, Readwise, Hardcover), 사전 순서",
"Required while Dictionaries sync is enabled": "사전 동기화가 켜져 있는 동안 필요",
"Resets in {{hours}} hr {{minutes}} min": "{{hours}}시간 {{minutes}}분 후 재설정",
"Pick a 4-digit PIN. You will need to enter it every time you open Readest. There is no way to recover a forgotten PIN. Clearing the app data is the only way to reset it.": "4자리 PIN을 선택하세요. Readest를 열 때마다 입력해야 합니다. 잊어버린 PIN을 복구할 방법은 없습니다. 앱 데이터를 지우는 것이 PIN을 재설정하는 유일한 방법입니다.",
"Credentials": "자격 증명",
"Tokens, usernames, and passwords for OPDS, KOReader, Hardcover, and Readwise. When disabled, credentials remain on this device only and are never uploaded.": "OPDS, KOReader, Hardcover, Readwise의 토큰, 사용자 이름, 비밀번호입니다. 비활성화되면 자격 증명이 이 기기에만 남아 있고 절대 업로드되지 않습니다.",
"Sensitive synced fields are encrypted before upload. Set a passphrase now or later when encryption is needed.": "민감한 동기화 필드는 업로드 전에 암호화됩니다. 지금 또는 암호화가 필요할 때 나중에 암호 문구를 설정하세요.",
"Saved to this account. You will be prompted for it when decrypting credentials.": "이 계정에 저장되었습니다. 자격 증명을 복호화할 때 입력하라는 메시지가 표시됩니다.",
"Reading progress on this device differs from \"{{deviceName}}\".": "이 기기의 읽기 진행 상황이 \"{{deviceName}}\"와(과) 다릅니다.",
"This device": "이 기기",
"Red": "빨강",
"Yellow": "노랑",
"Green": "초록",
"Blue": "파랑",
"Violet": "보라",
"Edit color": "색상 편집",
"Add custom color": "사용자 지정 색상 추가",
"Add label": "라벨 추가",
"Choose color": "색상 선택",
"Connected to Hardcover. Last synced {{time}}.": "Hardcover에 연결됨. 마지막 동기화 {{time}}.",
"Integrations": "통합",
"Hardcover": "Hardcover",
"Connected to Readwise. Last synced {{time}}.": "Readwise에 연결됨. 마지막 동기화 {{time}}.",
"Readwise": "Readwise",
"Browse and download books from online catalogs.": "온라인 카탈로그에서 책을 둘러보고 다운로드하세요.",
"Connected as {{user}}": "{{user}}(으)로 연결됨",
"Not connected": "연결되지 않음",
"{{count}} catalog_other": "카탈로그 {{count}}개",
"No catalogs": "카탈로그 없음",
"Connect Readest to external services for sync, highlights, and catalogs.": "동기화, 하이라이트, 카탈로그를 위해 Readest를 외부 서비스에 연결하세요.",
"Reading Sync": "읽기 동기화",
"Content Sources": "콘텐츠 소스",
"Scroll tabs": "탭 스크롤",
"Catalog actions": "카탈로그 작업",
"File Content": "파일 내용",
"Start your library": "서재 만들기",
"Pick a book from your device to add it to your library.": "기기에서 책을 골라 서재에 추가하세요.",
"Drop a book anywhere on this window, or pick one from your computer.": "이 창의 아무 곳에나 책을 놓거나, 컴퓨터에서 골라 보세요.",
"Sign in to sync your library": "로그인하여 서재 동기화"
}
@@ -25,9 +25,6 @@
"Your OTP token": "Token OTP anda",
"Verify token": "Sahkan token",
"Go Back": "Kembali",
"Sign in with Google": "Log masuk dengan Google",
"Sign in with Apple": "Log masuk dengan Apple",
"Sign in with GitHub": "Log masuk dengan GitHub",
"New Password": "Kata Laluan Baharu",
"Your new password": "Kata laluan baharu anda",
"Update password": "Kemas kini kata laluan",
@@ -163,7 +160,6 @@
"Failed to delete cloud backup of the book: {{title}}": "Gagal memadam sandaran awan buku: {{title}}",
"Failed to delete local copy of the book: {{title}}": "Gagal memadam salinan tempatan buku: {{title}}",
"Library Header": "Pengepala Perpustakaan",
"Welcome to your library. You can import your books here and read them anytime.": "Selamat datang ke perpustakaan anda. Anda boleh import buku anda di sini dan membacanya bila-bila masa.",
"Copied to notebook": "Disalin ke buku nota",
"No annotations to export": "Tiada anotasi untuk dieksport",
"Exported from Readest": "Dieksport dari Readest",
@@ -215,13 +211,9 @@
"Header Bar": "Bar Pengepala",
"View Options": "Pilihan Paparan",
"Sync Conflict": "Konflik Segerak",
"Sync reading progress from \"{{deviceName}}\"?": "Segerakkan kemajuan membaca dari \"{{deviceName}}\"?",
"another device": "peranti lain",
"Local Progress": "Kemajuan Tempatan",
"Remote Progress": "Kemajuan Jauh",
"Failed to connect": "Gagal menyambung",
"Disconnected": "Terputus",
"KOReader Sync Settings": "Tetapan Segerak KOReader",
"Sync as {{userDisplayName}}": "Segerak sebagai {{userDisplayName}}",
"Sync Server Connected": "Pelayan Segerak Disambung",
"Sync Strategy": "Strategi Segerak",
@@ -230,7 +222,6 @@
"Send changes only": "Hantar perubahan sahaja",
"Receive changes only": "Terima perubahan sahaja",
"Checksum Method": "Kaedah Semak Jumlah",
"File Content (recommended)": "Kandungan Fail (disyorkan)",
"File Name": "Nama Fail",
"Device Name": "Nama Peranti",
"Connect to your KOReader Sync server.": "Sambung ke pelayan Segerak KOReader anda.",
@@ -251,8 +242,8 @@
"Add your notes here...": "Tambah nota anda di sini...",
"Search notes and excerpts...": "Cari nota dan petikan...",
"{{time}} min left in chapter": "{{time}} min lagi dalam bab",
"{{number}} pages left in chapter": "{{number}} halaman lagi dalam bab",
"{{count}} pages left in chapter_other": "{{count}} halaman lagi dalam bab",
"{{number}} pages left in chapter": "<0>{{number}}</0><1> halaman lagi dalam bab</1>",
"{{count}} pages left in chapter_other": "<0>{{count}}</0><1> halaman lagi dalam bab</1>",
"On {{current}} of {{total}} page": "Di halaman {{current}} daripada {{total}}",
"Unable to open book": "Tidak dapat membuka buku",
"Section Title": "Tajuk Seksyen",
@@ -296,7 +287,6 @@
"Next Paragraph": "Perenggan Seterusnya",
"Read Aloud": "Baca Dengan Kuat",
"Ready to read aloud": "Bersedia untuk baca dengan kuat",
"TTS not supported for PDF": "TTS tidak disokong untuk PDF",
"TTS not supported for this document": "TTS tidak disokong untuk dokumen ini",
"No Timeout": "Tiada Had Masa",
"{{value}} minute": "{{value}} minit",
@@ -319,7 +309,6 @@
"Separate Cover Page": "Halaman Kulit Berasingan",
"Scrolled Mode": "Mod Tatal",
"Sign in to Sync": "Log masuk untuk Segerak",
"Synced at {{time}}": "Disegerak pada {{time}}",
"Never synced": "Tidak pernah disegerak",
"Invert Image In Dark Mode": "Songsangkan Imej Dalam Mod Gelap",
"Failed to auto-save book cover for lock screen: {{error}}": "Gagal auto-simpan kulit buku untuk skrin kunci: {{error}}",
@@ -500,11 +489,9 @@
"Strikethrough": "Garis Lorek",
"Squiggly": "Berkelok-kelok",
"Outline": "Garis Besar",
"Save Current Color": "Simpan Warna Semasa",
"Quick Colors": "Warna Pantas",
"Override Book Color": "Ganti Warna Buku",
"Scroll": "Tatal",
"Continuous Scroll": "Tatal Berterusan",
"Overlap Pixels": "Piksel Bertindih",
"Pagination": "Penomboran Halaman",
"Tap to Paginate": "Ketik untuk Nomborkan Halaman",
@@ -520,7 +507,6 @@
"Paging Animation": "Animasi Halaman",
"Device": "Peranti",
"E-Ink Mode": "Mod E-Ink",
"Auto Screen Brightness": "Kecerahan Skrin Auto",
"Security": "Keselamatan",
"Allow JavaScript": "Benarkan JavaScript",
"Enable only if you trust the file.": "Aktifkan hanya jika anda mempercayai fail.",
@@ -649,8 +635,8 @@
"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",
"Source Han Serif CN": "Source Han Serif CN",
"Huiwen-MinchoGBK": "Huiwen Mincho",
"KingHwa_OldSong": "KingHwa_OldSong",
"Google Books": "Google Books",
"Open Library": "Open Library",
@@ -695,9 +681,7 @@
"Validating...": "Mengesahkan...",
"View All": "Lihat Semua",
"Forward": "Maju",
"OPDS Catalog": "Katalog OPDS",
"Home": "Laman Utama",
"Library": "Perpustakaan",
"{{count}} items_other": "{{count}} item",
"Download completed": "Muat turun selesai",
"Download failed": "Muat turun gagal",
@@ -716,5 +700,692 @@
"Last": "Terakhir",
"Cannot Load Page": "Tidak dapat memuatkan halaman",
"An error occurred": "Ralat telah berlaku",
"Online Library": "Perpustakaan Dalam Talian"
"Online Library": "Perpustakaan Dalam Talian",
"URL must start with http:// or https://": "URL mesti bermula dengan http:// atau https://",
"Title, Author, Tag, etc...": "Tajuk, Pengarang, Tag, dll...",
"Query": "Pertanyaan",
"Subject": "Subjek",
"Enter {{terms}}": "Masukkan {{terms}}",
"No search results found": "Tiada hasil carian ditemui",
"Failed to load OPDS feed: {{status}} {{statusText}}": "Gagal memuatkan suapan OPDS: {{status}} {{statusText}}",
"Search in {{title}}": "Cari dalam {{title}}",
"Manage Storage": "Urus Penyimpanan",
"Failed to load files": "Gagal memuatkan fail",
"Deleted {{count}} file(s)_other": "{{count}} fail telah dipadam",
"Failed to delete {{count}} file(s)_other": "Gagal memadam {{count}} fail",
"Failed to delete files": "Gagal memadam fail",
"Total Files": "Jumlah Fail",
"Total Size": "Jumlah Saiz",
"Quota": "Kuota",
"Used": "Digunakan",
"Files": "Fail",
"Search files...": "Cari fail...",
"Newest First": "Terbaru dahulu",
"Oldest First": "Tertua dahulu",
"Largest First": "Terbesar dahulu",
"Smallest First": "Terkecil dahulu",
"Name A-Z": "Nama A-Z",
"Name Z-A": "Nama Z-A",
"{{count}} selected_other": "{{count}} dipilih",
"Delete Selected": "Padam Dipilih",
"Created": "Dicipta",
"No files found": "Tiada fail dijumpai",
"No files uploaded yet": "Belum ada fail dimuat naik",
"files": "fail",
"Page {{current}} of {{total}}": "Halaman {{current}} daripada {{total}}",
"Are you sure to delete {{count}} selected file(s)?_other": "Adakah anda pasti mahu memadam {{count}} fail yang dipilih?",
"Cloud Storage Usage": "Penggunaan Storan Awan",
"Rename Group": "Namakan Semula Kumpulan",
"From Directory": "Dari Direktori",
"Successfully imported {{count}} book(s)_other": "Berjaya mengimport {{count}} buku",
"Count": "Jumlah",
"Start Page": "Halaman Awal",
"Search in OPDS Catalog...": "Cari dalam Katalog OPDS...",
"Please log in to use advanced TTS features": "Sila log masuk untuk menggunakan ciri TTS lanjutan",
"Word limit of 30 words exceeded.": "Had 30 perkataan telah dilebihi.",
"Proofread": "Semakan",
"Current selection": "Pilihan semasa",
"All occurrences in this book": "Semua kejadian dalam buku ini",
"All occurrences in your library": "Semua kejadian dalam perpustakaan anda",
"Selected text:": "Teks dipilih:",
"Replace with:": "Ganti dengan:",
"Enter text...": "Masukkan teks…",
"Case sensitive:": "Peka huruf besar dan kecil:",
"Scope:": "Skop:",
"Selection": "Pilihan",
"Library": "Perpustakaan",
"Yes": "Ya",
"No": "Tidak",
"Proofread Replacement Rules": "Peraturan penggantian semakan",
"Selected Text Rules": "Peraturan teks dipilih",
"No selected text replacement rules": "Tiada peraturan penggantian untuk teks dipilih",
"Book Specific Rules": "Peraturan khusus buku",
"No book-level replacement rules": "Tiada peraturan penggantian peringkat buku",
"Disable Quick Action": "Nyahaktifkan Tindakan Pantas",
"Enable Quick Action on Selection": "Aktifkan Tindakan Pantas pada Pilihan",
"None": "Tiada",
"Annotation Tools": "Alat Anotasi",
"Enable Quick Actions": "Aktifkan Tindakan Pantas",
"Quick Action": "Tindakan Pantas",
"Copy to Notebook": "Salin ke Buku Nota",
"Copy text after selection": "Salin teks selepas pilihan",
"Highlight text after selection": "Sorot teks selepas pilihan",
"Annotate text after selection": "Anotasi teks selepas pilihan",
"Search text after selection": "Cari teks selepas pilihan",
"Look up text in dictionary after selection": "Cari teks dalam kamus selepas pilihan",
"Translate text after selection": "Terjemah teks selepas pilihan",
"Read text aloud after selection": "Baca teks dengan kuat selepas pilihan",
"Proofread text after selection": "Semak teks selepas pilihan",
"{{activeCount}} active, {{pendingCount}} pending": "{{activeCount}} aktif, {{pendingCount}} menunggu",
"{{failedCount}} failed": "{{failedCount}} gagal",
"Waiting...": "Menunggu...",
"Failed": "Gagal",
"Completed": "Selesai",
"Cancelled": "Dibatalkan",
"Retry": "Cuba lagi",
"Active": "Aktif",
"Transfer Queue": "Baris Gilir Pemindahan",
"Upload All": "Muat Naik Semua",
"Download All": "Muat Turun Semua",
"Resume Transfers": "Sambung Pemindahan",
"Pause Transfers": "Jeda Pemindahan",
"Pending": "Menunggu",
"No transfers": "Tiada pemindahan",
"Retry All": "Cuba Semua Lagi",
"Clear Completed": "Kosongkan Selesai",
"Clear Failed": "Kosongkan Gagal",
"Upload queued: {{title}}": "Muat naik dalam baris gilir: {{title}}",
"Download queued: {{title}}": "Muat turun dalam baris gilir: {{title}}",
"Book not found in library": "Buku tidak dijumpai dalam perpustakaan",
"Unknown error": "Ralat tidak diketahui",
"Please log in to continue": "Sila log masuk untuk meneruskan",
"Cloud File Transfers": "Pemindahan Fail Awan",
"Show Search Results": "Tunjukkan hasil carian",
"Search results for '{{term}}'": "Hasil untuk '{{term}}'",
"Close Search": "Tutup carian",
"Previous Result": "Hasil sebelumnya",
"Next Result": "Hasil seterusnya",
"Bookmarks": "Penanda buku",
"Annotations": "Anotasi",
"Show Results": "Tunjukkan Hasil",
"Clear search": "Kosongkan carian",
"Clear search history": "Kosongkan sejarah carian",
"Tap to Toggle Footer": "Ketik untuk togol pengaki",
"Show Current Time": "Tunjukkan Waktu Sekarang",
"Use 24 Hour Clock": "Gunakan Format 24 Jam",
"Show Current Battery Status": "Paparkan Status Bateri Semasa",
"Exported successfully": "Berjaya dieksport",
"Book exported successfully.": "Buku berjaya dieksport.",
"Failed to export the book.": "Gagal mengeksport buku.",
"Export Book": "Eksport Buku",
"Whole word:": "Perkataan penuh:",
"Date Published": "Tarikh diterbitkan",
"Only for TTS:": "Hanya untuk TTS:",
"Uploaded": "Dimuat naik",
"Downloaded": "Dimuat turun",
"Deleted": "Dipadam",
"Note:": "Nota:",
"Time:": "Masa:",
"Format Options": "Pilihan Format",
"Export Date": "Tarikh Eksport",
"Chapter Titles": "Tajuk Bab",
"Chapter Separator": "Pemisah Bab",
"Highlights": "Serlahan",
"Note Date": "Tarikh Nota",
"Advanced": "Lanjutan",
"Hide": "Sembunyikan",
"Show": "Tunjukkan",
"Use Custom Template": "Gunakan Templat Tersuai",
"Export Template": "Templat Eksport",
"Template Syntax:": "Sintaks Templat:",
"Insert value": "Masukkan nilai",
"Format date (locale)": "Format tarikh (tempatan)",
"Format date (custom)": "Format tarikh (tersuai)",
"Conditional": "Bersyarat",
"Loop": "Gelung",
"Available Variables:": "Pembolehubah Tersedia:",
"Book title": "Tajuk buku",
"Book author": "Pengarang buku",
"Export date": "Tarikh eksport",
"Array of chapters": "Senarai bab",
"Chapter title": "Tajuk bab",
"Array of annotations": "Senarai anotasi",
"Highlighted text": "Teks yang diserlahkan",
"Annotation note": "Nota anotasi",
"Date Format Tokens:": "Token Format Tarikh:",
"Year (4 digits)": "Tahun (4 digit)",
"Month (01-12)": "Bulan (01-12)",
"Day (01-31)": "Hari (01-31)",
"Hour (00-23)": "Jam (00-23)",
"Minute (00-59)": "Minit (00-59)",
"Second (00-59)": "Saat (00-59)",
"Show Source": "Tunjukkan Sumber",
"No content to preview": "Tiada kandungan untuk pratonton",
"Export": "Eksport",
"Set Timeout": "Tetapkan masa tamat",
"Select Voice": "Pilih suara",
"Toggle Sticky Bottom TTS Bar": "Togol bar TTS melekit",
"Display what I'm reading on Discord": "Papar buku yang sedang dibaca di Discord",
"Show on Discord": "Papar di Discord",
"Instant {{action}}": "{{action}} Segera",
"Instant {{action}} Disabled": "{{action}} Segera Dilumpuhkan",
"Annotation": "Anotasi",
"Reset Template": "Tetapkan Semula Templat",
"Annotation style": "Gaya anotasi",
"Annotation color": "Warna anotasi",
"Annotation time": "Masa anotasi",
"AI": "AI",
"Are you sure you want to re-index this book?": "Adakah anda pasti mahu mengindeks semula buku ini?",
"Enable AI in Settings": "Aktifkan AI dalam Tetapan",
"Index This Book": "Indeks Buku Ini",
"Enable AI search and chat for this book": "Aktifkan carian AI dan sembang untuk buku ini",
"Start Indexing": "Mula Mengindeks",
"Indexing book...": "Mengindeks buku...",
"Preparing...": "Menyediakan...",
"Delete this conversation?": "Padam perbualan ini?",
"No conversations yet": "Tiada perbualan lagi",
"Start a new chat to ask questions about this book": "Mulakan sembang baharu untuk bertanya tentang buku ini",
"Rename": "Namakan Semula",
"New Chat": "Sembang Baharu",
"Chat": "Sembang",
"Please enter a model ID": "Sila masukkan ID model",
"Model not available or invalid": "Model tidak tersedia atau tidak sah",
"Failed to validate model": "Gagal mengesahkan model",
"Couldn't connect to Ollama. Is it running?": "Tidak dapat menyambung ke Ollama. Adakah ia sedang berjalan?",
"Invalid API key or connection failed": "Kunci API tidak sah atau sambungan gagal",
"Connection failed": "Sambungan gagal",
"AI Assistant": "Pembantu AI",
"Enable AI Assistant": "Aktifkan Pembantu AI",
"Provider": "Pembekal",
"Ollama (Local)": "Ollama (Tempatan)",
"AI Gateway (Cloud)": "Gateway AI (Awan)",
"Ollama Configuration": "Konfigurasi Ollama",
"Refresh Models": "Muat Semula Model",
"AI Model": "Model AI",
"No models detected": "Tiada model dikesan",
"AI Gateway Configuration": "Konfigurasi Gateway AI",
"Choose from a selection of high-quality, economical AI models. You can also bring your own model by selecting \"Custom Model\" below.": "Pilih dari pelbagai model AI berkualiti tinggi dan ekonomi. Anda juga boleh menggunakan model anda sendiri dengan memilih \"Model Tersuai\" di bawah.",
"API Key": "Kunci API",
"Get Key": "Dapatkan Kunci",
"Model": "Model",
"Custom Model...": "Model Tersuai...",
"Custom Model ID": "ID Model Tersuai",
"Validate": "Sahkan",
"Model available": "Model tersedia",
"Connection": "Sambungan",
"Test Connection": "Uji Sambungan",
"Connected": "Disambung",
"Custom Colors": "Warna Tersuai",
"Color E-Ink Mode": "Mod E-Ink Warna",
"Reading Ruler": "Pembaris Membaca",
"Enable Reading Ruler": "Dayakan Pembaris Membaca",
"Lines to Highlight": "Baris untuk Diserlahkan",
"Ruler Color": "Warna Pembaris",
"Command Palette": "Palet Perintah",
"Search settings and actions...": "Cari tetapan dan tindakan...",
"No results found for": "Tiada hasil dijumpai untuk",
"Type to search settings and actions": "Taip untuk mencari tetapan dan tindakan",
"Recent": "Baru-baru ini",
"navigate": "navigasi",
"select": "pilih",
"close": "tutup",
"Search Settings": "Cari Tetapan",
"Page Margins": "Margin Halaman",
"AI Provider": "Penyedia AI",
"Ollama URL": "URL Ollama",
"Ollama Model": "Model Ollama",
"AI Gateway Model": "Model AI Gateway",
"Actions": "Tindakan",
"Navigation": "Navigasi",
"Set status for {{count}} book(s)_other": "Tetapkan status untuk {{count}} buku",
"Mark as Unread": "Tandakan sebagai Belum Dibaca",
"Mark as Finished": "Tandakan sebagai Selesai",
"Finished": "Selesai",
"Unread": "Belum Dibaca",
"Clear Status": "Kosongkan Status",
"Status": "Status",
"Loading": "Memuatkan...",
"Exit Paragraph Mode": "Keluar dari Mod Perenggan",
"Paragraph Mode": "Mod Perenggan",
"Embedding Model": "Model Benaman",
"{{count}} book(s) synced_other": "{{count}} buku telah disegerakkan",
"Unable to start RSVP": "Tidak dapat memulakan RSVP",
"RSVP not supported for PDF": "RSVP tidak disokong untuk PDF",
"Select Chapter": "Pilih Bab",
"Context": "Konteks",
"Ready": "Sedia",
"Chapter Progress": "Kemajuan Bab",
"words": "perkataan",
"{{time}} left": "{{time}} tinggal",
"Reading progress": "Kemajuan membaca",
"Skip back 15 words": "Langkau belakang 15 perkataan",
"Back 15 words (Shift+Left)": "Belakang 15 perkataan (Shift+Kiri)",
"Pause (Space)": "Jeda (Ruang)",
"Play (Space)": "Main (Ruang)",
"Skip forward 15 words": "Langkau depan 15 perkataan",
"Forward 15 words (Shift+Right)": "Depan 15 perkataan (Shift+Kanan)",
"Decrease speed": "Kurangkan kelajuan",
"Slower (Left/Down)": "Lebih perlahan (Kiri/Bawah)",
"Increase speed": "Tingkatkan kelajuan",
"Faster (Right/Up)": "Lebih cepat (Kanan/Atas)",
"Start RSVP Reading": "Mulakan Pembacaan RSVP",
"Choose where to start reading": "Pilih tempat untuk mula membaca",
"From Chapter Start": "Dari Permulaan Bab",
"Start reading from the beginning of the chapter": "Mula membaca dari awal bab",
"Resume": "Sambung",
"Continue from where you left off": "Sambung dari tempat anda berhenti",
"From Current Page": "Dari Halaman Semasa",
"Start from where you are currently reading": "Mula dari tempat anda sedang membaca",
"From Selection": "Dari Pilihan",
"Speed Reading Mode": "Mod Bacaan Pantas",
"Scroll left": "Tatal ke kiri",
"Scroll right": "Tatal ke kanan",
"Library Sync Progress": "Kemajuan Segerak Perpustakaan",
"Back to library": "Kembali ke perpustakaan",
"Group by...": "Kumpulan mengikut...",
"Export as Plain Text": "Eksport sebagai Teks Biasa",
"Export as Markdown": "Eksport sebagai Markdown",
"Show Page Navigation Buttons": "Butang navigasi",
"Page {{number}}": "Halaman {{number}}",
"highlight": "serlah",
"underline": "garis bawah",
"squiggly": "berombak",
"red": "merah",
"violet": "ungu",
"blue": "biru",
"green": "hijau",
"yellow": "kuning",
"Select {{style}} style": "Pilih gaya {{style}}",
"Select {{color}} color": "Pilih warna {{color}}",
"Close Book": "Tutup Buku",
"Speed Reading": "Bacaan Pantas",
"Close Speed Reading": "Tutup Bacaan Pantas",
"Authors": "Penulis",
"Books": "Buku",
"Groups": "Kumpulan",
"Back to TTS Location": "Kembali ke Lokasi TTS",
"Metadata": "Metadata",
"Image viewer": "Penyorot imej",
"Previous Image": "Imej Sebelumnya",
"Next Image": "Imej Seterusnya",
"Zoomed": "Dizum",
"Zoom level": "Tahap zum",
"Table viewer": "Penyorot jadual",
"Unable to connect to Readwise. Please check your network connection.": "Tidak dapat menyambung ke Readwise. Sila periksa sambungan rangkaian anda.",
"Invalid Readwise access token": "Token akses Readwise tidak sah",
"Disconnected from Readwise": "Terputus sambungan daripada Readwise",
"Never": "Tidak pernah",
"Sync Enabled": "Penyinkronan Didayakan",
"Disconnect": "Putuskan sambungan",
"Connect your Readwise account to sync highlights.": "Sambungkan akaun Readwise anda untuk menyinkronkan sorotan.",
"Get your access token at": "Dapatkan token akses anda di",
"Access Token": "Token Akses",
"Paste your Readwise access token": "Tampal token akses Readwise anda",
"Readwise Sync": "Penyinkronan Readwise",
"Push Highlights": "Hantar Sorotan",
"Highlights synced to Readwise": "Sorotan disinkronkan ke Readwise",
"Readwise sync failed: no internet connection": "Penyinkronan Readwise gagal: tiada sambungan internet",
"Readwise sync failed: {{error}}": "Penyinkronan Readwise gagal: {{error}}",
"System Screen Brightness": "Kecerahan Skrin Sistem",
"Page:": "Halaman:",
"Page: {{number}}": "Halaman: {{number}}",
"Annotation page number": "Nombor halaman anotasi",
"Translating...": "Menterjemah...",
"Show Battery Percentage": "Tunjukkan Peratusan Bateri",
"Hide Scrollbar": "Sembunyikan Bar Tatal",
"Skip to last reading position": "Lompat ke posisi bacaan terakhir",
"Show context": "Tunjukkan konteks",
"Hide context": "Sembunyikan konteks",
"Decrease font size": "Kecilkan saiz fon",
"Increase font size": "Besarkan saiz fon",
"Focus": "Fokus",
"Theme color": "Warna tema",
"Import failed": "Import gagal",
"Available Formatters:": "Pemformat Tersedia:",
"Format date": "Format tarikh",
"Markdown block quote (> per line)": "Petikan blok Markdown (> setiap baris)",
"Newlines to <br>": "Baris baharu ke <br>",
"Change case": "Tukar huruf besar/kecil",
"Trim whitespace": "Pangkas ruang kosong",
"Truncate to n characters": "Potong kepada n aksara",
"Replace text": "Ganti teks",
"Fallback value": "Nilai lalai",
"Get length": "Dapatkan panjang",
"First/last element": "Elemen pertama/terakhir",
"Join array": "Gabung tatasusunan",
"Backup failed: {{error}}": "Sandaran gagal: {{error}}",
"Select Backup": "Pilih Sandaran",
"Restore failed: {{error}}": "Pemulihan gagal: {{error}}",
"Backup & Restore": "Sandaran & Pemulihan",
"Create a backup of your library or restore from a previous backup. Restoring will merge with your current library.": "Buat sandaran perpustakaan anda atau pulihkan daripada sandaran sebelumnya. Pemulihan akan digabungkan dengan perpustakaan semasa anda.",
"Backup Library": "Sandarkan Perpustakaan",
"Restore Library": "Pulihkan Perpustakaan",
"Creating backup...": "Membuat sandaran...",
"Restoring library...": "Memulihkan perpustakaan...",
"{{current}} of {{total}} items": "{{current}} daripada {{total}} item",
"Backup completed successfully!": "Sandaran berjaya!",
"Restore completed successfully!": "Pemulihan berjaya!",
"Your library has been saved to the selected location.": "Perpustakaan anda telah disimpan di lokasi yang dipilih.",
"{{added}} books added, {{updated}} books updated.": "{{added}} buku ditambah, {{updated}} buku dikemas kini.",
"Operation failed": "Operasi gagal",
"Loading library...": "Memuatkan perpustakaan...",
"{{count}} books refreshed_other": "{{count}} buku dimuat semula",
"Failed to refresh metadata": "Gagal memuat semula metadata",
"Refresh Metadata": "Muat Semula Metadata",
"Keyboard Shortcuts": "Pintasan Papan Kekunci",
"View all keyboard shortcuts": "Lihat semua pintasan papan kekunci",
"Switch Sidebar Tab": "Tukar tab bar sisi",
"Toggle Notebook": "Tunjuk/sembunyikan buku nota",
"Search in Book": "Cari dalam buku",
"Toggle Scroll Mode": "Togol mod tatal",
"Toggle Select Mode": "Togol mod pilih",
"Toggle Bookmark": "Togol penanda buku",
"Toggle Text to Speech": "Togol teks ke pertuturan",
"Play / Pause TTS": "Main / Jeda TTS",
"Toggle Paragraph Mode": "Togol mod perenggan",
"Highlight Selection": "Serlahkan pilihan",
"Underline Selection": "Garis bawah pilihan",
"Annotate Selection": "Anotasi pilihan",
"Search Selection": "Cari pilihan",
"Copy Selection": "Salin pilihan",
"Translate Selection": "Terjemah pilihan",
"Dictionary Lookup": "Cari dalam kamus",
"Read Aloud Selection": "Baca kuat pilihan",
"Proofread Selection": "Semak pilihan",
"Open Settings": "Buka tetapan",
"Open Command Palette": "Buka palet arahan",
"Show Keyboard Shortcuts": "Tunjukkan pintasan papan kekunci",
"Open Books": "Buka buku",
"Toggle Fullscreen": "Togol skrin penuh",
"Close Window": "Tutup tetingkap",
"Quit App": "Keluar dari aplikasi",
"Go Left / Previous Page": "Ke kiri / Halaman sebelumnya",
"Go Right / Next Page": "Ke kanan / Halaman seterusnya",
"Go Up": "Ke atas",
"Go Down": "Ke bawah",
"Previous Chapter": "Bab sebelumnya",
"Next Chapter": "Bab seterusnya",
"Scroll Half Page Down": "Tatal separuh halaman ke bawah",
"Scroll Half Page Up": "Tatal separuh halaman ke atas",
"Save Note": "Simpan nota",
"Single Section Scroll": "Tatal Satu Bahagian",
"General": "Umum",
"Text to Speech": "Teks ke Pertuturan",
"Zoom": "Zum",
"Window": "Tetingkap",
"Your Bookshelf": "Rak Buku Anda",
"TTS": "Teks ke Suara",
"Media Info": "Info Media",
"Update Frequency": "Kekerapan Kemas Kini",
"Every Sentence": "Setiap Ayat",
"Every Paragraph": "Setiap Perenggan",
"Every Chapter": "Setiap Bab",
"TTS Media Info Update Frequency": "Kekerapan Kemas Kini Info Media TTS",
"Select reading speed": "Pilih kelajuan membaca",
"Drag to seek": "Seret untuk mencari",
"Punctuation Delay": "Kelewatan tanda baca",
"Please confirm that this OPDS connection will be proxied through Readest servers on the web app before continuing.": "Sila sahkan bahawa sambungan OPDS ini akan diproksi melalui pelayan Readest pada aplikasi web sebelum meneruskan.",
"Custom Headers (optional)": "Pengepala Tersuai (pilihan)",
"Add one header per line using \"Header-Name: value\".": "Tambah satu pengepala setiap baris menggunakan \"Header-Name: value\".",
"I understand this OPDS connection will be proxied through Readest servers on the web app. If I do not trust Readest with these credentials or headers, I should use the native app instead.": "Saya faham bahawa sambungan OPDS ini akan diproksi melalui pelayan Readest pada aplikasi web. Jika saya tidak mempercayai Readest dengan kelayakan atau pengepala ini, saya perlu menggunakan aplikasi natif.",
"Unable to connect to Hardcover. Please check your network connection.": "Tidak dapat menyambung ke Hardcover. Sila semak sambungan rangkaian anda.",
"Invalid Hardcover API token": "Token API Hardcover tidak sah",
"Disconnected from Hardcover": "Diputuskan dari Hardcover",
"Connect your Hardcover account to sync reading progress and notes.": "Sambungkan akaun Hardcover anda untuk menyegerakkan kemajuan bacaan dan nota.",
"Get your API token from hardcover.app → Settings → API.": "Dapatkan token API anda dari hardcover.app → Tetapan → API.",
"API Token": "Token API",
"Paste your Hardcover API token": "Tampal token API Hardcover anda",
"Hardcover Sync": "Penyegerakan Hardcover",
"Push Notes": "Hantar Nota",
"No annotations or excerpts to sync for this book.": "Tiada anotasi atau petikan untuk disegerakkan bagi buku ini.",
"Configure Hardcover in Settings first.": "Konfigurasikan Hardcover dalam Tetapan terlebih dahulu.",
"No new Hardcover note changes to sync.": "Tiada perubahan nota Hardcover baharu untuk disegerakkan.",
"Hardcover synced: {{inserted}} new, {{updated}} updated, {{skipped}} unchanged": "Hardcover disegerakkan: {{inserted}} baharu, {{updated}} dikemas kini, {{skipped}} tidak berubah",
"Hardcover notes sync failed: {{error}}": "Penyegerakan nota Hardcover gagal: {{error}}",
"Reading progress synced to Hardcover": "Kemajuan bacaan disegerakkan ke Hardcover",
"Hardcover progress sync failed: {{error}}": "Penyegerakan kemajuan Hardcover gagal: {{error}}",
"ISBN": "ISBN",
"Keep existing source identifier": "Kekalkan pengecam sumber sedia ada",
"Toggle Toolbar": "Togol Bar Alat",
"Split Hyphens": "Pisahkan Sempang",
"Apply Theme Colors to PDF": "Gunakan Warna Tema pada PDF",
"Name": "Nama",
"Remove": "Alih keluar",
"Edit OPDS Catalog": "Edit Katalog OPDS",
"Save Changes": "Simpan Perubahan",
"Use Book Layout": "Guna susun atur buku",
"End of this section. Continue to the next.": "Akhir bahagian ini. Teruskan ke bahagian seterusnya.",
"Auto-download": "Muat turun automatik",
"Auto-download new items": "Muat turun item baharu secara automatik",
"Automatically download new publications when the app syncs": "Muat turun penerbitan baharu secara automatik apabila apl disegerakkan",
"{{count}} new item(s) downloaded from OPDS_other": "{{count}} item baharu dimuat turun dari OPDS",
"Failed to sync {{count}} OPDS catalog(s)_other": "Gagal menyegerak {{count}} katalog OPDS",
"Last synced {{when}}": "Selaras kali terakhir {{when}}",
"Failed downloads": "Muat turun gagal",
"No failed downloads": "Tiada muat turun gagal",
"Attempts: {{count}}_other": "Percubaan: {{count}}",
"Skip": "Langkau",
"Skip all": "Langkau semua",
"Retry all": "Cuba lagi semua",
"{{count}} failed_other": "{{count}} gagal",
"(none)": "(tiada)",
"Identifiers": "Pengenal",
"Read (Stream)": "Baca (Strim)",
"Failed to start stream": "Gagal memulakan strim",
"No dictionaries enabled": "Tiada kamus diaktifkan",
"Enable a dictionary in Settings → Language → Dictionaries.": "Aktifkan kamus di Tetapan → Bahasa → Kamus.",
"Wiktionary": "Wiktionary",
"Drag to reorder": "Seret untuk susun semula",
"Built-in": "Terbina dalam",
"Bundle is missing on this device. Re-import to use it.": "Bundle tiada pada peranti ini. Import semula untuk menggunakannya.",
"This dictionary format is not supported.": "Format kamus ini tidak disokong.",
"MDict": "MDict",
"StarDict": "StarDict",
"Imported {{count}} dictionary_other": "{{count}} kamus diimport",
"Skipped incomplete bundles: {{names}}": "Bundle tidak lengkap dilangkau: {{names}}",
"Failed to import dictionary: {{message}}": "Gagal mengimport kamus: {{message}}",
"Dictionaries": "Kamus",
"Delete Dictionary": "Padam Kamus",
"Importing…": "Mengimport…",
"Import Dictionary": "Import Kamus",
"No dictionaries available.": "Tiada kamus tersedia.",
"StarDict bundles need .ifo, .idx, and .dict.dz files (.syn optional).": "Bundle StarDict memerlukan fail .ifo, .idx dan .dict.dz (.syn pilihan).",
"Select all the bundle files together when importing.": "Pilih semua fail bundle sekali gus semasa mengimport.",
"Manage Dictionaries": "Urus Kamus",
"Select Dictionary Files": "Pilih Fail Kamus",
"Read on Wikipedia →": "Baca di Wikipedia →",
"This link can't be opened": "Pautan ini tidak boleh dibuka",
"The annotation link is missing required information. The original link may have been truncated.": "Pautan anotasi tiada maklumat yang diperlukan. Pautan asal mungkin telah dipotong.",
"Go to Readest": "Pergi ke Readest",
"Open-source ebook reader for everyone, on every device.": "Pembaca e-buku sumber terbuka untuk semua orang, pada setiap peranti.",
"Open in Readest": "Buka dalam Readest",
"If Readest didn't open automatically, choose an option below:": "Jika Readest tidak dibuka secara automatik, pilih pilihan di bawah:",
"Continue reading where you left off.": "Sambung membaca dari tempat anda berhenti.",
"Readest logo": "Logo Readest",
"Opening Readest...": "Membuka Readest…",
"Open in Readest app": "Buka dalam aplikasi Readest",
"Continue in browser": "Teruskan dalam pelayar",
"Don't have Readest?": "Tiada Readest?",
"Book not in your library": "Buku tiada dalam pustaka anda",
"Share Book": "Kongsi Buku",
"Could not create share link": "Tidak dapat mencipta pautan kongsi",
"Link copied": "Pautan disalin",
"Could not copy link": "Tidak dapat menyalin pautan",
"Share revoked": "Perkongsian dibatalkan",
"Could not revoke share": "Tidak dapat membatalkan perkongsian",
"Uploading book…": "Memuat naik buku…",
"Generating…": "Menjana…",
"Generate share link": "Jana pautan kongsi",
"Share URL": "URL Kongsi",
"Copy link": "Salin pautan",
"Copied": "Disalin",
"Share via…": "Kongsi melalui…",
"Expires {{date}}": "Tamat pada {{date}}",
"Revoking…": "Membatalkan…",
"Revoke share": "Batalkan perkongsian",
"Sign in to share books": "Log masuk untuk berkongsi buku",
"Expiring soon": "Akan tamat tidak lama lagi",
"Missing share token": "Token kongsi hilang",
"Could not add to your library": "Tidak dapat ditambah ke pustaka anda",
"This share link is no longer available": "Pautan kongsi ini tidak lagi tersedia",
"The original link may have expired or been revoked.": "Pautan asal mungkin telah tamat tempoh atau dibatalkan.",
"Get Readest": "Dapatkan Readest",
"Loading shared book…": "Memuat buku yang dikongsi…",
"Adding…": "Menambah…",
"Add to my library": "Tambah ke pustaka saya",
"Could not load your shares": "Tidak dapat memuat perkongsian anda",
"Revoked": "Dibatalkan",
"Expired": "Tamat tempoh",
"Shared books": "Buku yang dikongsi",
"You haven't shared any books yet": "Anda belum berkongsi sebarang buku",
"Open a book and tap Share to send it to a friend.": "Buka buku dan ketik Kongsi untuk menghantarnya kepada rakan.",
"{{count}} active_other": "{{count}} aktif",
"{{count}} downloads_other": "{{count}} muat turun",
"starts at saved page": "mula dari halaman tersimpan",
"More actions": "Tindakan lain",
"Loading…": "Memuat…",
"Load more": "Muat lagi",
"Could not load shared book": "Tidak dapat memuat buku yang dikongsi",
"The share link is missing required information.": "Pautan kongsi tidak mempunyai maklumat yang diperlukan.",
"Please check your connection and try again.": "Sila semak sambungan anda dan cuba lagi.",
"Sign in to import shared books": "Log masuk untuk mengimport buku yang dikongsi",
"Already in your library": "Sudah ada dalam pustaka anda",
"Added to your library": "Ditambah ke pustaka anda",
"Could not import shared book": "Tidak dapat mengimport buku yang dikongsi",
"Manage Shared Links": "Urus pautan kongsi",
"Expires in": "Tamat dalam",
"{{count}} days_other": "{{count}} hari",
"Expires in {{count}} days_other": "Tamat dalam {{count}} hari",
"Expires in {{count}} hours_other": "Tamat dalam {{count}} jam",
"Open in app": "Buka dalam app",
"Shared with you": "Dikongsi dengan anda",
"Downloading… {{percent}}%": "Memuat turun… {{percent}}%",
"Import progress": "Kemajuan import",
"Share reading progress": "Kongsi kemajuan bacaan",
"Includes your reading progress": "Termasuk kemajuan bacaan anda",
"Synced {{time}}": "Disegerakkan {{time}}",
"Sync Info": "Maklumat penyegerakan",
"Last Synced": "Penyegerakan terakhir",
"DICT": "DICT",
"Slob": "Slob",
"DICT bundles need a .index file and a .dict.dz file.": "Pakej DICT memerlukan fail .index dan fail .dict.dz.",
"Slob bundles need a .slob file.": "Pakej Slob memerlukan fail .slob.",
"URL template must start with http(s):// and contain %WORD%.": "Templat URL mesti bermula dengan http(s):// dan mengandungi %WORD%.",
"Web": "Web",
"Add Web Search": "Tambah Carian Web",
"Edit Web Search": "Edit Carian Web",
"e.g. Google": "cth. Google",
"URL Template": "Templat URL",
"Use %WORD% where the looked-up word should appear.": "Gunakan %WORD% di mana perkataan yang dicari harus muncul.",
"Open the search result in your browser:": "Buka hasil carian dalam pelayar anda:",
"Open in {{name}}": "Buka dalam {{name}}",
"Book Fingerprint": "Cap jari buku",
"Search the web": "Cari di web",
"App deeplink (readest://)": "Pautan dalam apl (readest://)",
"Universal web link (https://)": "Pautan web universal (https://)",
"Name cannot be empty.": "Nama tidak boleh kosong.",
"Replaced {{count}} existing dictionary_other": "Menggantikan {{count}} kamus sedia ada",
"Cancel Edit": "Batal Sunting",
"Edit Dictionary": "Sunting Kamus",
"MDict bundles use .mdx files; companion .mdd and .css files are optional.": "Bungkusan MDict menggunakan fail .mdx; fail .mdd dan .css yang menyertainya adalah pilihan.",
"Dictionary name": "Nama kamus",
"This audio can't play here — the dictionary uses an outdated format. Try one with Opus, MP3, or WAV audio.": "Audio ini tidak boleh dimainkan di sini — kamus menggunakan format lapuk. Cuba kamus dengan audio Opus, MP3 atau WAV.",
"File uploaded: {{title}}": "Fail dimuat naik: {{title}}",
"File downloaded: {{title}}": "Fail dimuat turun: {{title}}",
"Deleted cloud copy of the file: {{title}}": "Salinan awan fail dipadam: {{title}}",
"Failed to upload file: {{title}}": "Gagal memuat naik fail: {{title}}",
"Failed to download file: {{title}}": "Gagal memuat turun fail: {{title}}",
"Failed to delete cloud copy of the file: {{title}}": "Gagal memadam salinan awan fail: {{title}}",
"{{percentage}}% used": "{{percentage}}% telah digunakan",
"Set PIN…": "Tetapkan PIN…",
"Require a 4-digit PIN to open Readest": "Wajibkan PIN 4 digit untuk membuka Readest",
"Change PIN…": "Tukar PIN…",
"Disable PIN…": "Lumpuhkan PIN…",
"Sync passphrase ready": "Frasa laluan penyegerakan sedia",
"This permanently deletes the encrypted credentials we sync (e.g., OPDS catalog passwords) on every device. Local copies are preserved. You will need to re-enter the sync passphrase or set a new one. Continue?": "Ini memadamkan secara kekal bukti kelayakan tersulit yang kami selaraskan (cth., kata laluan katalog OPDS) pada setiap peranti. Salinan tempatan dikekalkan. Anda perlu memasukkan semula frasa laluan penyegerakan atau menetapkan yang baharu. Teruskan?",
"Sync passphrase forgotten — all encrypted fields cleared": "Frasa laluan penyegerakan dilupakan — semua medan tersulit dikosongkan",
"Sync passphrase": "Frasa laluan penyegerakan",
"Set passphrase": "Tetapkan frasa laluan",
"Forgot passphrase": "Lupa frasa laluan",
"Incorrect PIN": "PIN salah",
"App locked": "Aplikasi terkunci",
"Enter your PIN": "Masukkan PIN anda",
"Readest is locked. Enter your 4-digit PIN to continue.": "Readest terkunci. Masukkan PIN 4 digit anda untuk meneruskan.",
"PIN code": "Kod PIN",
"Forgetting your PIN locks you out of this device. You'll need to clear the app's data to reset it.": "Terlupa PIN akan mengunci anda daripada peranti ini. Anda perlu mengosongkan data aplikasi untuk menetapkannya semula.",
"Passphrase must be at least 8 characters": "Frasa laluan mestilah sekurang-kurangnya 8 aksara",
"Passphrases do not match": "Frasa laluan tidak sepadan",
"Set sync passphrase": "Tetapkan frasa laluan penyegerakan",
"Enter sync passphrase": "Masukkan frasa laluan penyegerakan",
"A sync passphrase encrypts your sensitive fields (like OPDS catalog credentials) before they sync. We never see this passphrase. Pick something memorable — there is no recovery without it.": "Frasa laluan penyegerakan menyulitkan medan sensitif anda (seperti bukti kelayakan katalog OPDS) sebelum ia diselaraskan. Kami tidak pernah melihat frasa laluan ini. Pilih sesuatu yang mudah diingati — tiada pemulihan tanpanya.",
"Enter the sync passphrase you set on another device to decrypt your synced credentials.": "Masukkan frasa laluan penyegerakan yang anda tetapkan pada peranti lain untuk menyahsulit bukti kelayakan yang disegerakkan.",
"Confirm passphrase": "Sahkan frasa laluan",
"Unlock": "Buka kunci",
"Set PIN": "Tetapkan PIN",
"Change PIN": "Tukar PIN",
"Disable PIN": "Lumpuhkan PIN",
"Enter your current PIN, then choose a new 4-digit PIN.": "Masukkan PIN semasa anda, kemudian pilih PIN 4 digit baharu.",
"Enter your current PIN to disable the app lock.": "Masukkan PIN semasa anda untuk melumpuhkan kunci aplikasi.",
"PIN must be {{length}} digits": "PIN mesti mengandungi {{length}} digit",
"PINs do not match": "PIN tidak sepadan",
"Current PIN": "PIN semasa",
"New PIN": "PIN baharu",
"Confirm new PIN": "Sahkan PIN baharu",
"Manage Sync": "Urus penyegerakan",
"Imported book files and library metadata": "Fail buku yang diimport dan metadata pustaka",
"Last-read position, bookmarks, and per-book preferences": "Kedudukan bacaan terakhir, penanda buku dan keutamaan setiap buku",
"Highlights and notes": "Sorotan dan nota",
"Fonts": "Fon",
"Custom font files": "Fail fon tersuai",
"Backgrounds": "Latar belakang",
"Custom background textures": "Tekstur latar belakang tersuai",
"OPDS catalogs": "Katalog OPDS",
"Saved catalog URLs and (encrypted) credentials": "URL katalog yang disimpan dan kelayakan (disulitkan)",
"Wrong sync passphrase — synced credentials could not be decrypted": "Frasa laluan penyegerakan salah — kelayakan yang disegerakkan tidak dapat dinyahsulit",
"Sync passphrase data on the server was reset. Re-encrypting your credentials under the new passphrase…": "Data frasa laluan penyegerakan pada pelayan telah ditetapkan semula. Menyulitkan semula kelayakan anda dengan frasa laluan baharu…",
"Failed to decrypt synced credentials": "Gagal menyahsulit kelayakan yang disegerakkan",
"Imported dictionary bundles and settings": "Himpunan kamus yang diimport dan tetapan",
"Choose what syncs across your devices. Disabling a category stops this device from sending or receiving rows of that kind. Anything already on the server is left alone, re-enabling resumes from where you stopped.": "Pilih apa yang disegerakkan antara peranti anda. Melumpuhkan kategori menghentikan peranti ini daripada menghantar atau menerima entri jenis itu. Apa-apa yang sudah ada pada pelayan dibiarkan, mengaktifkannya semula akan menyambung dari tempat anda berhenti.",
"Data Sync": "Penyegerakan Data",
"Manage Fonts": "Urus Fon",
"App settings": "Tetapan aplikasi",
"Theme, highlight colours, integrations (KOSync, Readwise, Hardcover), and dictionary order": "Tema, warna sorotan, integrasi (KOSync, Readwise, Hardcover), dan susunan kamus",
"Required while Dictionaries sync is enabled": "Diperlukan semasa penyegerakan Kamus didayakan",
"Resets in {{hours}} hr {{minutes}} min": "Set semula dalam {{hours}} j {{minutes}} min",
"Pick a 4-digit PIN. You will need to enter it every time you open Readest. There is no way to recover a forgotten PIN. Clearing the app data is the only way to reset it.": "Pilih PIN 4 digit. Anda perlu memasukkannya setiap kali anda membuka Readest. Tiada cara untuk memulihkan PIN yang terlupa. Mengosongkan data aplikasi adalah satu-satunya cara untuk menetapkannya semula.",
"Credentials": "Kelayakan",
"Tokens, usernames, and passwords for OPDS, KOReader, Hardcover, and Readwise. When disabled, credentials remain on this device only and are never uploaded.": "Token, nama pengguna dan kata laluan untuk OPDS, KOReader, Hardcover dan Readwise. Apabila dilumpuhkan, kelayakan hanya kekal pada peranti ini dan tidak pernah dimuat naik.",
"Sensitive synced fields are encrypted before upload. Set a passphrase now or later when encryption is needed.": "Medan disegerakkan yang sensitif disulitkan sebelum dimuat naik. Tetapkan frasa laluan sekarang atau kemudian apabila penyulitan diperlukan.",
"Saved to this account. You will be prompted for it when decrypting credentials.": "Disimpan dalam akaun ini. Anda akan diminta memasukkannya apabila kelayakan perlu dinyahsulit.",
"Reading progress on this device differs from \"{{deviceName}}\".": "Kemajuan membaca pada peranti ini berbeza daripada \"{{deviceName}}\".",
"This device": "Peranti ini",
"Red": "Merah",
"Yellow": "Kuning",
"Green": "Hijau",
"Blue": "Biru",
"Violet": "Ungu",
"Edit color": "Edit warna",
"Add custom color": "Tambah warna tersuai",
"Add label": "Tambah label",
"Choose color": "Pilih warna",
"Connected to Hardcover. Last synced {{time}}.": "Disambungkan ke Hardcover. Penyegerakan terakhir {{time}}.",
"Integrations": "Integrasi",
"Hardcover": "Hardcover",
"Connected to Readwise. Last synced {{time}}.": "Disambungkan ke Readwise. Penyegerakan terakhir {{time}}.",
"Readwise": "Readwise",
"Browse and download books from online catalogs.": "Lihat dan muat turun buku dari katalog dalam talian.",
"Connected as {{user}}": "Disambungkan sebagai {{user}}",
"Not connected": "Tidak disambungkan",
"{{count}} catalog_other": "{{count}} katalog",
"No catalogs": "Tiada katalog",
"Connect Readest to external services for sync, highlights, and catalogs.": "Sambungkan Readest ke perkhidmatan luar untuk penyegerakan, sorotan dan katalog.",
"Reading Sync": "Penyegerakan Bacaan",
"Content Sources": "Sumber Kandungan",
"Scroll tabs": "Skrol tab",
"Catalog actions": "Tindakan katalog",
"File Content": "Kandungan fail",
"Start your library": "Mulakan perpustakaan anda",
"Pick a book from your device to add it to your library.": "Pilih buku dari peranti anda untuk ditambahkan ke perpustakaan anda.",
"Drop a book anywhere on this window, or pick one from your computer.": "Lepaskan buku di mana-mana pada tetingkap ini, atau pilih satu dari komputer anda.",
"Sign in to sync your library": "Daftar masuk untuk menyegerakkan perpustakaan anda"
}
@@ -23,9 +23,6 @@
"Token": "Token",
"Your OTP token": "Uw OTP-token",
"Verify token": "Token verifiëren",
"Sign in with Google": "Inloggen met Google",
"Sign in with Apple": "Inloggen met Apple",
"Sign in with GitHub": "Inloggen met GitHub",
"New Password": "Nieuw wachtwoord",
"Your new password": "Uw nieuwe wachtwoord",
"Update password": "Wachtwoord bijwerken",
@@ -93,7 +90,6 @@
"Book deleted: {{title}}": "Boek verwijderd: {{title}}",
"Failed to delete book: {{title}}": "Verwijderen van boek mislukt: {{title}}",
"Your Library": "Uw bibliotheek",
"Welcome to your library. You can import your books here and read them anytime.": "Welkom bij uw bibliotheek. U kunt hier uw boeken importeren en ze op elk moment lezen.",
"Copied to notebook": "Gekopieerd naar notitieblok",
"No annotations to export": "Geen annotaties om te exporteren",
"Exported from Readest": "Geëxporteerd uit Readest",
@@ -182,7 +178,6 @@
"Portrait": "Portret",
"Landscape": "Landschap",
"Behavior": "Gedrag",
"Continuous Scroll": "Doorlopend scrollen",
"Volume Keys for Page Flip": "Volumetoetsen voor pagina omslaan",
"Apply": "Toepassen",
"Font": "Lettertype",
@@ -209,7 +204,6 @@
"TOC": "Inhoudsopgave",
"Table of Contents": "Inhoudsopgave",
"Sidebar": "Zijbalk",
"TTS not supported for PDF": "TTS niet ondersteund voor PDF",
"No Timeout": "Geen time-out",
"{{value}} minute": "{{value}} minuut",
"{{value}} minutes": "{{value}} minuten",
@@ -304,7 +298,6 @@
"No notes match your search": "Geen notities gevonden",
"Search notes and excerpts...": "Zoek notities...",
"Sign in to Sync": "Meld je aan om te synchroniseren",
"Synced at {{time}}": "Gesynchroniseerd om {{time}}",
"Never synced": "Nooit gesynchroniseerd",
"Show Remaining Time": "Toon resterende tijd",
"{{time}} min left in chapter": "Nog {{time}} min in hoofdstuk",
@@ -338,11 +331,11 @@
"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}} paginas in hoofdstuk",
"{{count}} pages left in chapter_one": "<1>Nog </1><0>{{count}}</0><1> pagina in hoofdstuk</1>",
"{{count}} pages left in chapter_other": "<1>Nog </1><0>{{count}}</0><1> paginas in hoofdstuk</1>",
"Show Remaining Pages": "Toon resterende paginas",
"Source Han Serif CN VF": "Source Han Serif",
"Huiwen-mincho": "Huiwen Mincho",
"Source Han Serif CN": "Source Han Serif",
"Huiwen-MinchoGBK": "Huiwen Mincho",
"KingHwa_OldSong": "KingHwa Song",
"Manage Subscription": "Abonnement beheren",
"Coming Soon": "Binnenkort beschikbaar",
@@ -466,14 +459,12 @@
"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",
"Checksum Method": "Checksum-methode",
"File Content (recommended)": "Bestandsinhoud (aanbevolen)",
"File Name": "Bestandsnaam",
"Device Name": "Apparaatnaam",
"Connect to your KOReader Sync server.": "Verbind met je KOReader Sync-server.",
@@ -484,10 +475,7 @@
"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}}%)",
@@ -602,11 +590,10 @@
"Size": "Grootte",
"Cover": "Omslag",
"Contain": "Bevatten",
"{{number}} pages left in chapter": "Nog {{number}} paginas in hoofdstuk",
"{{number}} pages left in chapter": "<1>Nog </1><0>{{number}}</0><1> paginas in hoofdstuk</1>",
"Device": "Apparaat",
"E-Ink Mode": "E-Ink Modus",
"Highlight Colors": "Markeerkleuren",
"Auto Screen Brightness": "Automatische Schermhelderheid",
"Pagination": "Paginering",
"Disable Double Tap": "Schakel Dubbel Tikken Uit",
"Tap to Paginate": "Tik om te Pagineren",
@@ -641,7 +628,6 @@
"Strikethrough": "Doorhalen",
"Squiggly": "Kronkelig",
"Outline": "Omtrek",
"Save Current Color": "Huidige kleur opslaan",
"Quick Colors": "Snelle kleuren",
"Highlighter": "Marker",
"Save Book Cover": "Opslaan boekomslag",
@@ -699,9 +685,7 @@
"Validating...": "Valideren...",
"View All": "Alles bekijken",
"Forward": "Verder",
"OPDS Catalog": "OPDS-catalogus",
"Home": "Startpagina",
"Library": "Bibliotheek",
"{{count}} items_one": "{{count}} item",
"{{count}} items_other": "{{count}} items",
"Download completed": "Download voltooid",
@@ -721,5 +705,712 @@
"Last": "Laatste",
"Cannot Load Page": "Pagina kan niet worden geladen",
"An error occurred": "Er is een fout opgetreden",
"Online Library": "Online Bibliotheek"
"Online Library": "Online Bibliotheek",
"URL must start with http:// or https://": "URL moet beginnen met http:// of https://",
"Title, Author, Tag, etc...": "Titel, Auteur, Tag, enzovoort...",
"Query": "Zoekopdracht",
"Subject": "Onderwerp",
"Enter {{terms}}": "Voer {{terms}} in",
"No search results found": "Geen zoekresultaten gevonden",
"Failed to load OPDS feed: {{status}} {{statusText}}": "Het laden van de OPDS-feed is mislukt: {{status}} {{statusText}}",
"Search in {{title}}": "Zoeken in {{title}}",
"Manage Storage": "Opslag beheren",
"Failed to load files": "Bestanden laden mislukt",
"Deleted {{count}} file(s)_one": "{{count}} bestand verwijderd",
"Deleted {{count}} file(s)_other": "{{count}} bestanden verwijderd",
"Failed to delete {{count}} file(s)_one": "Kon {{count}} bestand niet verwijderen",
"Failed to delete {{count}} file(s)_other": "Kon {{count}} bestanden niet verwijderen",
"Failed to delete files": "Bestanden verwijderen mislukt",
"Total Files": "Totaal aantal bestanden",
"Total Size": "Totale grootte",
"Quota": "Quota",
"Used": "Gebruikt",
"Files": "Bestanden",
"Search files...": "Bestanden zoeken...",
"Newest First": "Nieuwste eerst",
"Oldest First": "Oudste eerst",
"Largest First": "Grootste eerst",
"Smallest First": "Kleinste eerst",
"Name A-Z": "Naam A-Z",
"Name Z-A": "Naam Z-A",
"{{count}} selected_one": "{{count}} geselecteerd",
"{{count}} selected_other": "{{count}} geselecteerd",
"Delete Selected": "Verwijder geselecteerde",
"Created": "Gemaakt",
"No files found": "Geen bestanden gevonden",
"No files uploaded yet": "Nog geen bestanden geüpload",
"files": "bestanden",
"Page {{current}} of {{total}}": "Pagina {{current}} van {{total}}",
"Are you sure to delete {{count}} selected file(s)?_one": "Weet je zeker dat je {{count}} geselecteerd bestand wilt verwijderen?",
"Are you sure to delete {{count}} selected file(s)?_other": "Weet je zeker dat je {{count}} geselecteerde bestanden wilt verwijderen?",
"Cloud Storage Usage": "Cloudopslaggebruik",
"Rename Group": "Groep hernoemen",
"From Directory": "Vanuit map",
"Successfully imported {{count}} book(s)_one": "Succesvol 1 boek geïmporteerd",
"Successfully imported {{count}} book(s)_other": "Succesvol {{count}} boeken geïmporteerd",
"Count": "Aantal",
"Start Page": "Startpagina",
"Search in OPDS Catalog...": "Zoeken in OPDS-catalogus...",
"Please log in to use advanced TTS features": "Log in om geavanceerde TTS-functies te gebruiken",
"Word limit of 30 words exceeded.": "De limiet van 30 woorden is overschreden.",
"Proofread": "Correctie",
"Current selection": "Huidige selectie",
"All occurrences in this book": "Alle voorkomens in dit boek",
"All occurrences in your library": "Alle voorkomens in je bibliotheek",
"Selected text:": "Geselecteerde tekst:",
"Replace with:": "Vervangen door:",
"Enter text...": "Tekst invoeren…",
"Case sensitive:": "Hoofdlettergevoelig:",
"Scope:": "Bereik:",
"Selection": "Selectie",
"Library": "Bibliotheek",
"Yes": "Ja",
"No": "Nee",
"Proofread Replacement Rules": "Correctie-vervangingsregels",
"Selected Text Rules": "Regels voor geselecteerde tekst",
"No selected text replacement rules": "Geen vervangingsregels voor geselecteerde tekst",
"Book Specific Rules": "Boekspecifieke regels",
"No book-level replacement rules": "Geen vervangingsregels op boekniveau",
"Disable Quick Action": "Snelle actie uitschakelen",
"Enable Quick Action on Selection": "Snelle actie bij selectie inschakelen",
"None": "Geen",
"Annotation Tools": "Annotatiehulpmiddelen",
"Enable Quick Actions": "Snelle acties inschakelen",
"Quick Action": "Snelle actie",
"Copy to Notebook": "Kopiëren naar notitieboek",
"Copy text after selection": "Kopieer tekst na selectie",
"Highlight text after selection": "Markeer tekst na selectie",
"Annotate text after selection": "Annoteren tekst na selectie",
"Search text after selection": "Zoek tekst na selectie",
"Look up text in dictionary after selection": "Zoek tekst op in woordenboek na selectie",
"Translate text after selection": "Vertaal tekst na selectie",
"Read text aloud after selection": "Lees tekst hardop na selectie",
"Proofread text after selection": "Corrigeer tekst na selectie",
"{{activeCount}} active, {{pendingCount}} pending": "{{activeCount}} actief, {{pendingCount}} wachtend",
"{{failedCount}} failed": "{{failedCount}} mislukt",
"Waiting...": "Wachten...",
"Failed": "Mislukt",
"Completed": "Voltooid",
"Cancelled": "Geannuleerd",
"Retry": "Opnieuw proberen",
"Active": "Actief",
"Transfer Queue": "Overdrachtwachtrij",
"Upload All": "Alles uploaden",
"Download All": "Alles downloaden",
"Resume Transfers": "Overdrachten hervatten",
"Pause Transfers": "Overdrachten pauzeren",
"Pending": "Wachtend",
"No transfers": "Geen overdrachten",
"Retry All": "Alles opnieuw proberen",
"Clear Completed": "Voltooide wissen",
"Clear Failed": "Mislukte wissen",
"Upload queued: {{title}}": "Upload in wachtrij: {{title}}",
"Download queued: {{title}}": "Download in wachtrij: {{title}}",
"Book not found in library": "Boek niet gevonden in bibliotheek",
"Unknown error": "Onbekende fout",
"Please log in to continue": "Log in om door te gaan",
"Cloud File Transfers": "Cloud Bestandoverdrachten",
"Show Search Results": "Zoekresultaten tonen",
"Search results for '{{term}}'": "Resultaten voor '{{term}}'",
"Close Search": "Zoekopdracht sluiten",
"Previous Result": "Vorig resultaat",
"Next Result": "Volgend resultaat",
"Bookmarks": "Bladwijzers",
"Annotations": "Aantekeningen",
"Show Results": "Resultaten tonen",
"Clear search": "Zoekopdracht wissen",
"Clear search history": "Zoekgeschiedenis wissen",
"Tap to Toggle Footer": "Tik om voettekst te wisselen",
"Show Current Time": "Huidige tijd weergeven",
"Use 24 Hour Clock": "24-uurs klok gebruike",
"Show Current Battery Status": "Toon huidige batterijstatus",
"Exported successfully": "Succesvol geëxporteerd",
"Book exported successfully.": "Boek succesvol geëxporteerd.",
"Failed to export the book.": "Exporteren van het boek mislukt.",
"Export Book": "Boek exporteren",
"Whole word:": "Heel woord:",
"Date Published": "Publicatiedatum",
"Only for TTS:": "Alleen voor TTS:",
"Uploaded": "Geüpload",
"Downloaded": "Gedownload",
"Deleted": "Verwijderd",
"Note:": "Notitie:",
"Time:": "Tijd:",
"Format Options": "Formaatopties",
"Export Date": "Exportdatum",
"Chapter Titles": "Hoofdstuktitels",
"Chapter Separator": "Hoofdstukscheiding",
"Highlights": "Markeringen",
"Note Date": "Notitiedatum",
"Advanced": "Geavanceerd",
"Hide": "Verbergen",
"Show": "Tonen",
"Use Custom Template": "Aangepast sjabloon gebruiken",
"Export Template": "Exportsjabloon",
"Template Syntax:": "Sjabloonsyntaxis:",
"Insert value": "Waarde invoegen",
"Format date (locale)": "Datum opmaken (lokaal)",
"Format date (custom)": "Datum opmaken (aangepast)",
"Conditional": "Voorwaardelijk",
"Loop": "Lus",
"Available Variables:": "Beschikbare variabelen:",
"Book title": "Boektitel",
"Book author": "Boekauteur",
"Export date": "Exportdatum",
"Array of chapters": "Reeks hoofdstukken",
"Chapter title": "Hoofdstuktitel",
"Array of annotations": "Reeks annotaties",
"Highlighted text": "Gemarkeerde tekst",
"Annotation note": "Annotatienotitie",
"Date Format Tokens:": "Datumformaattokens:",
"Year (4 digits)": "Jaar (4 cijfers)",
"Month (01-12)": "Maand (01-12)",
"Day (01-31)": "Dag (01-31)",
"Hour (00-23)": "Uur (00-23)",
"Minute (00-59)": "Minuut (00-59)",
"Second (00-59)": "Seconde (00-59)",
"Show Source": "Bron tonen",
"No content to preview": "Geen inhoud om te bekijken",
"Export": "Exporteren",
"Set Timeout": "Time-out instellen",
"Select Voice": "Stem selecteren",
"Toggle Sticky Bottom TTS Bar": "Vaste TTS-balk schakelen",
"Display what I'm reading on Discord": "Toon wat ik lees op Discord",
"Show on Discord": "Toon op Discord",
"Instant {{action}}": "Directe {{action}}",
"Instant {{action}} Disabled": "Directe {{action}} uitgeschakeld",
"Annotation": "Annotatie",
"Reset Template": "Sjabloon resetten",
"Annotation style": "Annotatiestijl",
"Annotation color": "Annotatiekleur",
"Annotation time": "Annotatietijd",
"AI": "AI",
"Are you sure you want to re-index this book?": "Weet u zeker dat u dit boek opnieuw wilt indexeren?",
"Enable AI in Settings": "AI inschakelen in Instellingen",
"Index This Book": "Dit boek indexeren",
"Enable AI search and chat for this book": "AI-zoeken en chat voor dit boek inschakelen",
"Start Indexing": "Indexeren starten",
"Indexing book...": "Boek wordt geïndexeerd...",
"Preparing...": "Voorbereiden...",
"Delete this conversation?": "Dit gesprek verwijderen?",
"No conversations yet": "Nog geen gesprekken",
"Start a new chat to ask questions about this book": "Start een nieuwe chat om vragen te stellen over dit boek",
"Rename": "Hernoemen",
"New Chat": "Nieuwe chat",
"Chat": "Chat",
"Please enter a model ID": "Voer een model-ID in",
"Model not available or invalid": "Model niet beschikbaar of ongeldig",
"Failed to validate model": "Model valideren mislukt",
"Couldn't connect to Ollama. Is it running?": "Kon geen verbinding maken met Ollama. Is het actief?",
"Invalid API key or connection failed": "Ongeldige API-sleutel of verbinding mislukt",
"Connection failed": "Verbinding mislukt",
"AI Assistant": "AI-assistent",
"Enable AI Assistant": "AI-assistent inschakelen",
"Provider": "Provider",
"Ollama (Local)": "Ollama (Lokaal)",
"AI Gateway (Cloud)": "AI-gateway (Cloud)",
"Ollama Configuration": "Ollama-configuratie",
"Refresh Models": "Modellen vernieuwen",
"AI Model": "AI-model",
"No models detected": "Geen modellen gedetecteerd",
"AI Gateway Configuration": "AI-gateway configuratie",
"Choose from a selection of high-quality, economical AI models. You can also bring your own model by selecting \"Custom Model\" below.": "Kies uit een selectie van hoogwaardige, voordelige AI-modellen. U kunt ook uw eigen model gebruiken door hieronder \"Aangepast model\" te selecteren.",
"API Key": "API-sleutel",
"Get Key": "Sleutel ophalen",
"Model": "Model",
"Custom Model...": "Aangepast model...",
"Custom Model ID": "Aangepast model-ID",
"Validate": "Valideren",
"Model available": "Model beschikbaar",
"Connection": "Verbinding",
"Test Connection": "Verbinding testen",
"Connected": "Verbonden",
"Custom Colors": "Aangepaste kleuren",
"Color E-Ink Mode": "Kleur E-Ink modus",
"Reading Ruler": "Leesliniaal",
"Enable Reading Ruler": "Leesliniaal inschakelen",
"Lines to Highlight": "Regels om te markeren",
"Ruler Color": "Kleur van liniaal",
"Command Palette": "Opdrachtenpalet",
"Search settings and actions...": "Zoek instellingen en acties...",
"No results found for": "Geen resultaten gevonden voor",
"Type to search settings and actions": "Typ om instellingen en acties te zoeken",
"Recent": "Recent",
"navigate": "navigeren",
"select": "selecteren",
"close": "sluiten",
"Search Settings": "Instellingen zoeken",
"Page Margins": "Paginamarges",
"AI Provider": "AI-provider",
"Ollama URL": "Ollama-URL",
"Ollama Model": "Ollama-model",
"AI Gateway Model": "AI Gateway-model",
"Actions": "Acties",
"Navigation": "Navigatie",
"Set status for {{count}} book(s)_one": "Stel status in voor {{count}} boek",
"Set status for {{count}} book(s)_other": "Stel status in voor {{count}} boeken",
"Mark as Unread": "Markeren als ongelezen",
"Mark as Finished": "Markeren als voltooid",
"Finished": "Voltooid",
"Unread": "Ongelezen",
"Clear Status": "Status wissen",
"Status": "Status",
"Loading": "Laden...",
"Exit Paragraph Mode": "Alineamodus verlaten",
"Paragraph Mode": "Alineamodus",
"Embedding Model": "Insluitmodel",
"{{count}} book(s) synced_one": "{{count}} boek gesynchroniseerd",
"{{count}} book(s) synced_other": "{{count}} boeken gesynchroniseerd",
"Unable to start RSVP": "Kan RSVP niet starten",
"RSVP not supported for PDF": "RSVP niet ondersteund voor PDF",
"Select Chapter": "Hoofdstuk selecteren",
"Context": "Context",
"Ready": "Klaar",
"Chapter Progress": "Hoofdstukvoortgang",
"words": "woorden",
"{{time}} left": "{{time}} over",
"Reading progress": "Leesvoortgang",
"Skip back 15 words": "15 woorden terug",
"Back 15 words (Shift+Left)": "15 woorden terug (Shift+Links)",
"Pause (Space)": "Pauze (Spatie)",
"Play (Space)": "Afspelen (Spatie)",
"Skip forward 15 words": "15 woorden vooruit",
"Forward 15 words (Shift+Right)": "15 woorden vooruit (Shift+Rechts)",
"Decrease speed": "Snelheid verlagen",
"Slower (Left/Down)": "Langzamer (Links/Omlaag)",
"Increase speed": "Snelheid verhogen",
"Faster (Right/Up)": "Sneller (Rechts/Omhoog)",
"Start RSVP Reading": "Start RSVP-lezen",
"Choose where to start reading": "Kies waar u wilt beginnen met lezen",
"From Chapter Start": "Vanaf begin hoofdstuk",
"Start reading from the beginning of the chapter": "Begin met lezen aan het begin van het hoofdstuk",
"Resume": "Hervatten",
"Continue from where you left off": "Ga verder waar u was gebleven",
"From Current Page": "Vanaf huidige pagina",
"Start from where you are currently reading": "Begin waar u momenteel leest",
"From Selection": "Vanaf selectie",
"Speed Reading Mode": "Snelle leesmodus",
"Scroll left": "Naar links scrollen",
"Scroll right": "Naar rechts scrollen",
"Library Sync Progress": "Voortgang bibliotheeksynchronisatie",
"Back to library": "Terug naar bibliotheek",
"Group by...": "Groeperen op...",
"Export as Plain Text": "Exporteren als platte tekst",
"Export as Markdown": "Exporteren als Markdown",
"Show Page Navigation Buttons": "Navigatieknoppen",
"Page {{number}}": "Pagina {{number}}",
"highlight": "markering",
"underline": "onderstreping",
"squiggly": "gegolfd",
"red": "rood",
"violet": "paars",
"blue": "blauw",
"green": "groen",
"yellow": "geel",
"Select {{style}} style": "Selecteer {{style}} stijl",
"Select {{color}} color": "Selecteer {{color}} kleur",
"Close Book": "Boek sluiten",
"Speed Reading": "Snel lezen",
"Close Speed Reading": "Snel lezen sluiten",
"Authors": "Auteurs",
"Books": "Boeken",
"Groups": "Groepen",
"Back to TTS Location": "Terug naar TTS-locatie",
"Metadata": "Metadata",
"Image viewer": "Afbeeldingenviewer",
"Previous Image": "Vorige afbeelding",
"Next Image": "Volgende afbeelding",
"Zoomed": "Gezoomd",
"Zoom level": "Zoomniveau",
"Table viewer": "Tabelviewer",
"Unable to connect to Readwise. Please check your network connection.": "Kan geen verbinding maken met Readwise. Controleer uw netwerkverbinding.",
"Invalid Readwise access token": "Ongeldige Readwise-toegangstoken",
"Disconnected from Readwise": "Verbinding met Readwise verbroken",
"Never": "Nooit",
"Sync Enabled": "Synchronisatie ingeschakeld",
"Disconnect": "Verbinding verbreken",
"Connect your Readwise account to sync highlights.": "Verbind uw Readwise-account om markeringen te synchroniseren.",
"Get your access token at": "Verkrijg uw toegangstoken op",
"Access Token": "Toegangstoken",
"Paste your Readwise access token": "Plak uw Readwise-toegangstoken",
"Readwise Sync": "Readwise-synchronisatie",
"Push Highlights": "Markeringen verzenden",
"Highlights synced to Readwise": "Markeringen gesynchroniseerd met Readwise",
"Readwise sync failed: no internet connection": "Readwise-synchronisatie mislukt: geen internetverbinding",
"Readwise sync failed: {{error}}": "Readwise-synchronisatie mislukt: {{error}}",
"System Screen Brightness": "Systeemhelderheid van het scherm",
"Page:": "Pagina:",
"Page: {{number}}": "Pagina: {{number}}",
"Annotation page number": "Annotatiepaginanummer",
"Translating...": "Vertalen...",
"Show Battery Percentage": "Batterijpercentage weergeven",
"Hide Scrollbar": "Schuifbalk verbergen",
"Skip to last reading position": "Ga naar laatste leespositie",
"Show context": "Context tonen",
"Hide context": "Context verbergen",
"Decrease font size": "Lettergrootte verkleinen",
"Increase font size": "Lettergrootte vergroten",
"Focus": "Focus",
"Theme color": "Themakleur",
"Import failed": "Import mislukt",
"Available Formatters:": "Beschikbare opmaak:",
"Format date": "Datum opmaken",
"Markdown block quote (> per line)": "Markdown-blokcitaat (> per regel)",
"Newlines to <br>": "Regeleinden naar <br>",
"Change case": "Hoofdlettergebruik wijzigen",
"Trim whitespace": "Witruimte verwijderen",
"Truncate to n characters": "Inkorten tot n tekens",
"Replace text": "Tekst vervangen",
"Fallback value": "Terugvalwaarde",
"Get length": "Lengte ophalen",
"First/last element": "Eerste/laatste element",
"Join array": "Array samenvoegen",
"Backup failed: {{error}}": "Back-up mislukt: {{error}}",
"Select Backup": "Selecteer back-up",
"Restore failed: {{error}}": "Herstel mislukt: {{error}}",
"Backup & Restore": "Back-up & Herstel",
"Create a backup of your library or restore from a previous backup. Restoring will merge with your current library.": "Maak een back-up van uw bibliotheek of herstel vanuit een eerdere back-up. Herstel wordt samengevoegd met uw huidige bibliotheek.",
"Backup Library": "Bibliotheek back-uppen",
"Restore Library": "Bibliotheek herstellen",
"Creating backup...": "Back-up maken...",
"Restoring library...": "Bibliotheek herstellen...",
"{{current}} of {{total}} items": "{{current}} van {{total}} items",
"Backup completed successfully!": "Back-up succesvol voltooid!",
"Restore completed successfully!": "Herstel succesvol voltooid!",
"Your library has been saved to the selected location.": "Uw bibliotheek is opgeslagen op de geselecteerde locatie.",
"{{added}} books added, {{updated}} books updated.": "{{added}} boeken toegevoegd, {{updated}} boeken bijgewerkt.",
"Operation failed": "Bewerking mislukt",
"Loading library...": "Bibliotheek laden...",
"{{count}} books refreshed_one": "{{count}} boek vernieuwd",
"{{count}} books refreshed_other": "{{count}} boeken vernieuwd",
"Failed to refresh metadata": "Metadata vernieuwen mislukt",
"Refresh Metadata": "Metadata vernieuwen",
"Keyboard Shortcuts": "Sneltoetsen",
"View all keyboard shortcuts": "Alle sneltoetsen bekijken",
"Switch Sidebar Tab": "Zijbalktabblad wisselen",
"Toggle Notebook": "Notitieboek tonen/verbergen",
"Search in Book": "Zoeken in boek",
"Toggle Scroll Mode": "Scrollmodus wisselen",
"Toggle Select Mode": "Selectiemodus wisselen",
"Toggle Bookmark": "Bladwijzer wisselen",
"Toggle Text to Speech": "Tekst-naar-spraak wisselen",
"Play / Pause TTS": "TTS afspelen / pauzeren",
"Toggle Paragraph Mode": "Alineamodus wisselen",
"Highlight Selection": "Selectie markeren",
"Underline Selection": "Selectie onderstrepen",
"Annotate Selection": "Selectie annoteren",
"Search Selection": "Selectie zoeken",
"Copy Selection": "Selectie kopiëren",
"Translate Selection": "Selectie vertalen",
"Dictionary Lookup": "Opzoeken in woordenboek",
"Read Aloud Selection": "Selectie voorlezen",
"Proofread Selection": "Selectie proeflezen",
"Open Settings": "Instellingen openen",
"Open Command Palette": "Opdrachtenpalet openen",
"Show Keyboard Shortcuts": "Sneltoetsen tonen",
"Open Books": "Boeken openen",
"Toggle Fullscreen": "Volledig scherm wisselen",
"Close Window": "Venster sluiten",
"Quit App": "App afsluiten",
"Go Left / Previous Page": "Naar links / Vorige pagina",
"Go Right / Next Page": "Naar rechts / Volgende pagina",
"Go Up": "Omhoog",
"Go Down": "Omlaag",
"Previous Chapter": "Vorig hoofdstuk",
"Next Chapter": "Volgend hoofdstuk",
"Scroll Half Page Down": "Halve pagina naar beneden scrollen",
"Scroll Half Page Up": "Halve pagina naar boven scrollen",
"Save Note": "Notitie opslaan",
"Single Section Scroll": "Scrollen per sectie",
"General": "Algemeen",
"Text to Speech": "Tekst naar spraak",
"Zoom": "Zoom",
"Window": "Venster",
"Your Bookshelf": "Jouw boekenkast",
"TTS": "Spraak",
"Media Info": "Media-info",
"Update Frequency": "Updatefrequentie",
"Every Sentence": "Elke zin",
"Every Paragraph": "Elke alinea",
"Every Chapter": "Elk hoofdstuk",
"TTS Media Info Update Frequency": "TTS media-info updatefrequentie",
"Select reading speed": "Leessnelheid selecteren",
"Drag to seek": "Sleep om te zoeken",
"Punctuation Delay": "Leesteken-vertraging",
"Please confirm that this OPDS connection will be proxied through Readest servers on the web app before continuing.": "Bevestig dat deze OPDS-verbinding via Readest-servers in de webapp wordt doorgestuurd voordat u doorgaat.",
"Custom Headers (optional)": "Aangepaste headers (optioneel)",
"Add one header per line using \"Header-Name: value\".": "Voeg één header per regel toe met \"Header-Name: value\".",
"I understand this OPDS connection will be proxied through Readest servers on the web app. If I do not trust Readest with these credentials or headers, I should use the native app instead.": "Ik begrijp dat deze OPDS-verbinding via Readest-servers in de webapp wordt doorgestuurd. Als ik Readest niet vertrouw met deze inloggegevens of headers, moet ik de native app gebruiken.",
"Unable to connect to Hardcover. Please check your network connection.": "Kan geen verbinding maken met Hardcover. Controleer uw netwerkverbinding.",
"Invalid Hardcover API token": "Ongeldig Hardcover API-token",
"Disconnected from Hardcover": "Verbinding met Hardcover verbroken",
"Connect your Hardcover account to sync reading progress and notes.": "Verbind uw Hardcover-account om leesvoortgang en notities te synchroniseren.",
"Get your API token from hardcover.app → Settings → API.": "Haal uw API-token op via hardcover.app → Instellingen → API.",
"API Token": "API-token",
"Paste your Hardcover API token": "Plak uw Hardcover API-token",
"Hardcover Sync": "Hardcover-synchronisatie",
"Push Notes": "Notities verzenden",
"No annotations or excerpts to sync for this book.": "Geen annotaties of fragmenten om te synchroniseren voor dit boek.",
"Configure Hardcover in Settings first.": "Configureer eerst Hardcover in de instellingen.",
"No new Hardcover note changes to sync.": "Geen nieuwe Hardcover-notitiewijzigingen om te synchroniseren.",
"Hardcover synced: {{inserted}} new, {{updated}} updated, {{skipped}} unchanged": "Hardcover gesynchroniseerd: {{inserted}} nieuw, {{updated}} bijgewerkt, {{skipped}} ongewijzigd",
"Hardcover notes sync failed: {{error}}": "Synchronisatie van Hardcover-notities mislukt: {{error}}",
"Reading progress synced to Hardcover": "Leesvoortgang gesynchroniseerd met Hardcover",
"Hardcover progress sync failed: {{error}}": "Synchronisatie van Hardcover-voortgang mislukt: {{error}}",
"ISBN": "ISBN",
"Keep existing source identifier": "Bestaande bronidentificatie behouden",
"Toggle Toolbar": "Werkbalk in-/uitschakelen",
"Split Hyphens": "Afbreekstreepjes splitsen",
"Apply Theme Colors to PDF": "Themakleuren toepassen op PDF",
"Name": "Naam",
"Remove": "Verwijderen",
"Edit OPDS Catalog": "OPDS-catalogus bewerken",
"Save Changes": "Wijzigingen opslaan",
"Use Book Layout": "Boekindeling gebruiken",
"End of this section. Continue to the next.": "Einde van deze sectie. Ga verder naar de volgende.",
"Auto-download": "Automatisch downloaden",
"Auto-download new items": "Nieuwe items automatisch downloaden",
"Automatically download new publications when the app syncs": "Download automatisch nieuwe publicaties wanneer de app synchroniseert",
"{{count}} new item(s) downloaded from OPDS_one": "{{count}} nieuw item gedownload van OPDS",
"{{count}} new item(s) downloaded from OPDS_other": "{{count}} nieuwe items gedownload van OPDS",
"Failed to sync {{count}} OPDS catalog(s)_one": "Synchronisatie van {{count}} OPDS-catalogus mislukt",
"Failed to sync {{count}} OPDS catalog(s)_other": "Synchronisatie van {{count}} OPDS-catalogi mislukt",
"Last synced {{when}}": "Laatst gesynchroniseerd {{when}}",
"Failed downloads": "Mislukte downloads",
"No failed downloads": "Geen mislukte downloads",
"Attempts: {{count}}_one": "Pogingen: {{count}}",
"Attempts: {{count}}_other": "Pogingen: {{count}}",
"Skip": "Overslaan",
"Skip all": "Alles overslaan",
"Retry all": "Alles opnieuw proberen",
"{{count}} failed_one": "{{count}} mislukt",
"{{count}} failed_other": "{{count}} mislukt",
"(none)": "(geen)",
"Identifiers": "Identificatoren",
"Read (Stream)": "Lezen (Stream)",
"Failed to start stream": "Kan stream niet starten",
"No dictionaries enabled": "Geen woordenboeken ingeschakeld",
"Enable a dictionary in Settings → Language → Dictionaries.": "Schakel een woordenboek in via Instellingen → Taal → Woordenboeken.",
"Wiktionary": "Wiktionary",
"Drag to reorder": "Sleep om opnieuw te ordenen",
"Built-in": "Ingebouwd",
"Bundle is missing on this device. Re-import to use it.": "Bundel ontbreekt op dit apparaat. Importeer opnieuw om te gebruiken.",
"This dictionary format is not supported.": "Dit woordenboekformaat wordt niet ondersteund.",
"MDict": "MDict",
"StarDict": "StarDict",
"Imported {{count}} dictionary_one": "{{count}} woordenboek geïmporteerd",
"Imported {{count}} dictionary_other": "{{count}} woordenboeken geïmporteerd",
"Skipped incomplete bundles: {{names}}": "Onvolledige bundels overgeslagen: {{names}}",
"Failed to import dictionary: {{message}}": "Importeren van woordenboek mislukt: {{message}}",
"Dictionaries": "Woordenboeken",
"Delete Dictionary": "Woordenboek verwijderen",
"Importing…": "Bezig met importeren…",
"Import Dictionary": "Woordenboek importeren",
"No dictionaries available.": "Geen woordenboeken beschikbaar.",
"StarDict bundles need .ifo, .idx, and .dict.dz files (.syn optional).": "StarDict-bundels hebben .ifo-, .idx- en .dict.dz-bestanden nodig (.syn optioneel).",
"Select all the bundle files together when importing.": "Selecteer bij het importeren alle bundelbestanden tegelijk.",
"Manage Dictionaries": "Woordenboeken beheren",
"Select Dictionary Files": "Woordenboekbestanden kiezen",
"Read on Wikipedia →": "Lees verder op Wikipedia →",
"This link can't be opened": "Deze link kan niet worden geopend",
"The annotation link is missing required information. The original link may have been truncated.": "De annotatielink mist de vereiste gegevens. De oorspronkelijke link is mogelijk afgekapt.",
"Go to Readest": "Ga naar Readest",
"Open-source ebook reader for everyone, on every device.": "Open-source e-bookreader voor iedereen, op elk apparaat.",
"Open in Readest": "Openen in Readest",
"If Readest didn't open automatically, choose an option below:": "Als Readest niet automatisch is geopend, kies hieronder een optie:",
"Continue reading where you left off.": "Lees verder waar je was gebleven.",
"Readest logo": "Readest-logo",
"Opening Readest...": "Readest wordt geopend…",
"Open in Readest app": "Openen in de Readest-app",
"Continue in browser": "Doorgaan in de browser",
"Don't have Readest?": "Heb je Readest nog niet?",
"Book not in your library": "Boek staat niet in je bibliotheek",
"Share Book": "Boek delen",
"Could not create share link": "Kan deelkoppeling niet maken",
"Link copied": "Koppeling gekopieerd",
"Could not copy link": "Kan koppeling niet kopiëren",
"Share revoked": "Delen ingetrokken",
"Could not revoke share": "Kan delen niet intrekken",
"Uploading book…": "Boek uploaden…",
"Generating…": "Genereren…",
"Generate share link": "Deellink maken",
"Share URL": "Deel-URL",
"Copy link": "Koppeling kopiëren",
"Copied": "Gekopieerd",
"Share via…": "Delen via…",
"Expires {{date}}": "Verloopt op {{date}}",
"Revoking…": "Intrekken…",
"Revoke share": "Delen intrekken",
"Sign in to share books": "Meld u aan om boeken te delen",
"Expiring soon": "Verloopt binnenkort",
"Missing share token": "Deeltoken ontbreekt",
"Could not add to your library": "Kon niet aan je bibliotheek worden toegevoegd",
"This share link is no longer available": "Deze deelkoppeling is niet meer beschikbaar",
"The original link may have expired or been revoked.": "De oorspronkelijke koppeling is mogelijk verlopen of ingetrokken.",
"Get Readest": "Readest downloaden",
"Loading shared book…": "Gedeeld boek laden…",
"Adding…": "Toevoegen…",
"Add to my library": "Aan mijn bibliotheek toevoegen",
"Could not load your shares": "Kan uw delingen niet laden",
"Revoked": "Ingetrokken",
"Expired": "Verlopen",
"Shared books": "Gedeelde boeken",
"You haven't shared any books yet": "U heeft nog geen boeken gedeeld",
"Open a book and tap Share to send it to a friend.": "Open een boek en tik op Delen om het naar een vriend te sturen.",
"{{count}} active_one": "{{count}} actief",
"{{count}} active_other": "{{count}} actief",
"{{count}} downloads_one": "{{count}} download",
"{{count}} downloads_other": "{{count}} downloads",
"starts at saved page": "begint op opgeslagen pagina",
"More actions": "Meer acties",
"Loading…": "Laden…",
"Load more": "Meer laden",
"Could not load shared book": "Kan gedeeld boek niet laden",
"The share link is missing required information.": "In de deellink ontbreekt vereiste informatie.",
"Please check your connection and try again.": "Controleer uw verbinding en probeer het opnieuw.",
"Sign in to import shared books": "Meld u aan om gedeelde boeken te importeren",
"Already in your library": "Al in uw bibliotheek",
"Added to your library": "Toegevoegd aan uw bibliotheek",
"Could not import shared book": "Kan gedeeld boek niet importeren",
"Manage Shared Links": "Deelkoppelingen beheren",
"Expires in": "Verloopt over",
"{{count}} days_one": "1 dag",
"{{count}} days_other": "{{count}} dagen",
"Expires in {{count}} days_one": "Verloopt over 1 dag",
"Expires in {{count}} days_other": "Verloopt over {{count}} dagen",
"Expires in {{count}} hours_one": "Verloopt over 1 uur",
"Expires in {{count}} hours_other": "Verloopt over {{count}} uur",
"Open in app": "Openen in app",
"Shared with you": "Met u gedeeld",
"Downloading… {{percent}}%": "Downloaden… {{percent}}%",
"Import progress": "Voortgang import",
"Share reading progress": "Leesvoortgang delen",
"Includes your reading progress": "Inclusief je leesvoortgang",
"Synced {{time}}": "Gesynchroniseerd {{time}}",
"Sync Info": "Synchronisatie-info",
"Last Synced": "Laatst gesynchroniseerd",
"DICT": "DICT",
"Slob": "Slob",
"DICT bundles need a .index file and a .dict.dz file.": "DICT-bundels hebben een .index-bestand en een .dict.dz-bestand nodig.",
"Slob bundles need a .slob file.": "Slob-bundels hebben een .slob-bestand nodig.",
"URL template must start with http(s):// and contain %WORD%.": "URL-sjabloon moet beginnen met http(s):// en %WORD% bevatten.",
"Web": "Web",
"Add Web Search": "Webzoekopdracht toevoegen",
"Edit Web Search": "Webzoekopdracht bewerken",
"e.g. Google": "bijv. Google",
"URL Template": "URL-sjabloon",
"Use %WORD% where the looked-up word should appear.": "Gebruik %WORD% op de plek waar het opgezochte woord moet verschijnen.",
"Open the search result in your browser:": "Open het zoekresultaat in je browser:",
"Open in {{name}}": "Openen in {{name}}",
"Book Fingerprint": "Vingerafdruk van boek",
"Search the web": "Zoeken op het web",
"App deeplink (readest://)": "App-deeplink (readest://)",
"Universal web link (https://)": "Universele weblink (https://)",
"Name cannot be empty.": "Naam mag niet leeg zijn.",
"Replaced {{count}} existing dictionary_one": "{{count}} bestaand woordenboek vervangen",
"Replaced {{count}} existing dictionary_other": "{{count}} bestaande woordenboeken vervangen",
"Cancel Edit": "Bewerken annuleren",
"Edit Dictionary": "Woordenboek bewerken",
"MDict bundles use .mdx files; companion .mdd and .css files are optional.": "MDict-bundels gebruiken .mdx-bestanden; bijbehorende .mdd- en .css-bestanden zijn optioneel.",
"Dictionary name": "Naam van woordenboek",
"This audio can't play here — the dictionary uses an outdated format. Try one with Opus, MP3, or WAV audio.": "Deze audio kan hier niet worden afgespeeld — het woordenboek gebruikt een verouderd formaat. Probeer een woordenboek met Opus-, MP3- of WAV-audio.",
"File uploaded: {{title}}": "Bestand geüpload: {{title}}",
"File downloaded: {{title}}": "Bestand gedownload: {{title}}",
"Deleted cloud copy of the file: {{title}}": "Verwijderde cloudkopie van het bestand: {{title}}",
"Failed to upload file: {{title}}": "Uploaden van bestand mislukt: {{title}}",
"Failed to download file: {{title}}": "Downloaden van bestand mislukt: {{title}}",
"Failed to delete cloud copy of the file: {{title}}": "Verwijderen van cloudkopie van het bestand is mislukt: {{title}}",
"{{percentage}}% used": "{{percentage}}% gebruikt",
"Set PIN…": "PIN instellen…",
"Require a 4-digit PIN to open Readest": "4-cijferige PIN vereisen om Readest te openen",
"Change PIN…": "PIN wijzigen…",
"Disable PIN…": "PIN uitschakelen…",
"Sync passphrase ready": "Sync-wachtwoordzin gereed",
"This permanently deletes the encrypted credentials we sync (e.g., OPDS catalog passwords) on every device. Local copies are preserved. You will need to re-enter the sync passphrase or set a new one. Continue?": "Dit verwijdert permanent de versleutelde inloggegevens die we synchroniseren (bijv. OPDS-catalogi-wachtwoorden) op elk apparaat. Lokale kopieën blijven behouden. Je moet de sync-wachtwoordzin opnieuw invoeren of een nieuwe instellen. Doorgaan?",
"Sync passphrase forgotten — all encrypted fields cleared": "Sync-wachtwoordzin vergeten — alle versleutelde velden gewist",
"Sync passphrase": "Sync-wachtwoordzin",
"Set passphrase": "Wachtwoordzin instellen",
"Forgot passphrase": "Wachtwoordzin vergeten",
"Incorrect PIN": "Onjuiste PIN",
"App locked": "App vergrendeld",
"Enter your PIN": "Voer uw PIN in",
"Readest is locked. Enter your 4-digit PIN to continue.": "Readest is vergrendeld. Voer uw 4-cijferige PIN in om door te gaan.",
"PIN code": "PIN-code",
"Forgetting your PIN locks you out of this device. You'll need to clear the app's data to reset it.": "Als u uw PIN vergeet, wordt u buitengesloten van dit apparaat. U moet de app-gegevens wissen om hem opnieuw in te stellen.",
"Passphrase must be at least 8 characters": "De wachtwoordzin moet minstens 8 tekens lang zijn",
"Passphrases do not match": "Wachtwoordzinnen komen niet overeen",
"Set sync passphrase": "Sync-wachtwoordzin instellen",
"Enter sync passphrase": "Sync-wachtwoordzin invoeren",
"A sync passphrase encrypts your sensitive fields (like OPDS catalog credentials) before they sync. We never see this passphrase. Pick something memorable — there is no recovery without it.": "Een sync-wachtwoordzin versleutelt je gevoelige velden (zoals OPDS-cataloguscredentials) voordat ze worden gesynchroniseerd. We zien deze wachtwoordzin nooit. Kies iets dat je goed kunt onthouden — zonder de zin is er geen herstel mogelijk.",
"Enter the sync passphrase you set on another device to decrypt your synced credentials.": "Voer de sync-wachtwoordzin in die je op een ander apparaat hebt ingesteld om je gesynchroniseerde inloggegevens te ontsleutelen.",
"Confirm passphrase": "Wachtwoordzin bevestigen",
"Unlock": "Ontgrendelen",
"Set PIN": "PIN instellen",
"Change PIN": "PIN wijzigen",
"Disable PIN": "PIN uitschakelen",
"Enter your current PIN, then choose a new 4-digit PIN.": "Voer uw huidige PIN in en kies vervolgens een nieuwe 4-cijferige PIN.",
"Enter your current PIN to disable the app lock.": "Voer uw huidige PIN in om de app-vergrendeling uit te schakelen.",
"PIN must be {{length}} digits": "PIN moet uit {{length}} cijfers bestaan",
"PINs do not match": "PINs komen niet overeen",
"Current PIN": "Huidige PIN",
"New PIN": "Nieuwe PIN",
"Confirm new PIN": "Nieuwe PIN bevestigen",
"Manage Sync": "Synchronisatie beheren",
"Imported book files and library metadata": "Geïmporteerde boekbestanden en bibliotheekmetadata",
"Last-read position, bookmarks, and per-book preferences": "Laatst gelezen positie, bladwijzers en voorkeuren per boek",
"Highlights and notes": "Markeringen en notities",
"Fonts": "Lettertypen",
"Custom font files": "Aangepaste lettertypebestanden",
"Backgrounds": "Achtergronden",
"Custom background textures": "Aangepaste achtergrondtexturen",
"OPDS catalogs": "OPDS-catalogi",
"Saved catalog URLs and (encrypted) credentials": "Opgeslagen catalogus-URL's en (versleutelde) inloggegevens",
"Wrong sync passphrase — synced credentials could not be decrypted": "Onjuiste synchronisatiewachtwoordzin — gesynchroniseerde inloggegevens konden niet worden ontsleuteld",
"Sync passphrase data on the server was reset. Re-encrypting your credentials under the new passphrase…": "Synchronisatiewachtwoordzingegevens op de server zijn gereset. Inloggegevens worden opnieuw versleuteld met de nieuwe wachtwoordzin…",
"Failed to decrypt synced credentials": "Kan gesynchroniseerde inloggegevens niet ontsleutelen",
"Imported dictionary bundles and settings": "Geïmporteerde woordenboekpakketten en instellingen",
"Choose what syncs across your devices. Disabling a category stops this device from sending or receiving rows of that kind. Anything already on the server is left alone, re-enabling resumes from where you stopped.": "Kies wat er tussen je apparaten wordt gesynchroniseerd. Een categorie uitschakelen zorgt dat dit apparaat geen items van dat type meer verstuurt of ontvangt. Alles wat al op de server staat blijft ongewijzigd, en bij opnieuw inschakelen wordt verdergegaan waar je was gestopt.",
"Data Sync": "Gegevenssynchronisatie",
"Manage Fonts": "Lettertypen beheren",
"App settings": "App-instellingen",
"Theme, highlight colours, integrations (KOSync, Readwise, Hardcover), and dictionary order": "Thema, markeringskleuren, integraties (KOSync, Readwise, Hardcover) en woordenboekvolgorde",
"Required while Dictionaries sync is enabled": "Vereist zolang synchronisatie van Woordenboeken is ingeschakeld",
"Resets in {{hours}} hr {{minutes}} min": "Reset over {{hours}} u {{minutes}} min",
"Pick a 4-digit PIN. You will need to enter it every time you open Readest. There is no way to recover a forgotten PIN. Clearing the app data is the only way to reset it.": "Kies een viercijferige pincode. Je moet deze elke keer invoeren wanneer je Readest opent. Een vergeten pincode kan niet worden hersteld. Het wissen van de app-gegevens is de enige manier om deze opnieuw in te stellen.",
"Credentials": "Inloggegevens",
"Tokens, usernames, and passwords for OPDS, KOReader, Hardcover, and Readwise. When disabled, credentials remain on this device only and are never uploaded.": "Tokens, gebruikersnamen en wachtwoorden voor OPDS, KOReader, Hardcover en Readwise. Wanneer uitgeschakeld blijven inloggegevens alleen op dit apparaat en worden ze nooit geüpload.",
"Sensitive synced fields are encrypted before upload. Set a passphrase now or later when encryption is needed.": "Gevoelige gesynchroniseerde velden worden vóór het uploaden versleuteld. Stel nu of later een wachtwoordzin in wanneer versleuteling nodig is.",
"Saved to this account. You will be prompted for it when decrypting credentials.": "Opgeslagen op dit account. Er wordt om gevraagd wanneer inloggegevens ontsleuteld moeten worden.",
"Reading progress on this device differs from \"{{deviceName}}\".": "De leesvoortgang op dit apparaat verschilt van \"{{deviceName}}\".",
"This device": "Dit apparaat",
"Red": "Rood",
"Yellow": "Geel",
"Green": "Groen",
"Blue": "Blauw",
"Violet": "Violet",
"Edit color": "Kleur bewerken",
"Add custom color": "Aangepaste kleur toevoegen",
"Add label": "Label toevoegen",
"Choose color": "Kleur kiezen",
"Connected to Hardcover. Last synced {{time}}.": "Verbonden met Hardcover. Laatst gesynchroniseerd {{time}}.",
"Integrations": "Integraties",
"Hardcover": "Hardcover",
"Connected to Readwise. Last synced {{time}}.": "Verbonden met Readwise. Laatst gesynchroniseerd {{time}}.",
"Readwise": "Readwise",
"Browse and download books from online catalogs.": "Blader door online catalogi en download boeken.",
"Connected as {{user}}": "Verbonden als {{user}}",
"Not connected": "Niet verbonden",
"{{count}} catalog_one": "{{count}} catalogus",
"{{count}} catalog_other": "{{count}} catalogi",
"No catalogs": "Geen catalogi",
"Connect Readest to external services for sync, highlights, and catalogs.": "Verbind Readest met externe diensten voor synchronisatie, markeringen en catalogi.",
"Reading Sync": "Leessynchronisatie",
"Content Sources": "Inhoudsbronnen",
"Scroll tabs": "Tabbladen scrollen",
"Catalog actions": "Catalogusacties",
"File Content": "Bestandsinhoud",
"Start your library": "Begin je bibliotheek",
"Pick a book from your device to add it to your library.": "Kies een boek van je apparaat om aan je bibliotheek toe te voegen.",
"Drop a book anywhere on this window, or pick one from your computer.": "Sleep een boek ergens in dit venster, of kies er een van je computer.",
"Sign in to sync your library": "Meld je aan om je bibliotheek te synchroniseren"
}
@@ -102,11 +102,9 @@
"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.",
"Wikipedia": "Wikipedia",
"Writing Mode": "Tryb pisania",
"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",
"Apply to All Books": "Zastosuj do wszystkich książek",
"Apply to This Book": "Zastosuj do tej książki",
@@ -173,9 +171,6 @@
"Token": "Token",
"Your OTP token": "Twój OTP token",
"Verify token": "Weryfikacja tokena",
"Sign in with Google": "Zaloguj się za pomocą Google",
"Sign in with Apple": "Zaloguj się za pomocą Apple",
"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.",
"Community Support": "Wsparcie społeczności",
@@ -188,7 +183,6 @@
"RTL Direction": "Kierunek RTL",
"Maximum Column Height": "Maksymalna wysokość kolumny",
"Maximum Column Width": "Maksymalna szerokość kolumny",
"Continuous Scroll": "Przewijanie ciągłe",
"Fullscreen": "Pełny ekran",
"No supported files found. Supported formats: {{formats}}": "Nie znaleziono obsługiwanych plików. Obsługiwane formaty: {{formats}}",
"Drop to Import Books": "Upuść, aby zaimportować książki",
@@ -308,7 +302,6 @@
"No notes match your search": "Nie znaleziono notatek",
"Search notes and excerpts...": "Szukaj notatek...",
"Sign in to Sync": "Zaloguj się",
"Synced at {{time}}": "Sync: {{time}}",
"Never synced": "Brak synchronizacji",
"Show Remaining Time": "Pokazuj pozostały czas",
"{{time}} min left in chapter": "{{time}} min pozostało do końca rozdziału",
@@ -344,13 +337,13 @@
"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",
"{{count}} pages left in chapter_one": "<1>Pozostała </1><0>{{count}}</0><1> strona w rozdziale</1>",
"{{count}} pages left in chapter_few": "<1>Pozostały </1><0>{{count}}</0><1> strony w rozdziale</1>",
"{{count}} pages left in chapter_many": "<1>Pozostało </1><0>{{count}}</0><1> stron w rozdziale</1>",
"{{count}} pages left in chapter_other": "<1>Pozostało </1><0>{{count}}</0><1> stron w rozdziale</1>",
"Show Remaining Pages": "Pokaż pozostałe strony",
"Source Han Serif CN VF": "Source Han Serif",
"Huiwen-mincho": "Huiwen Mincho",
"Source Han Serif CN": "Source Han Serif",
"Huiwen-MinchoGBK": "Huiwen Mincho",
"KingHwa_OldSong": "KingHwa Song",
"Manage Subscription": "Zarządzaj subskrypcją",
"Coming Soon": "Wkrótce dostępne",
@@ -474,14 +467,12 @@
"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",
"Checksum Method": "Metoda sumy kontrolnej",
"File Content (recommended)": "Zawartość pliku (zalecane)",
"File Name": "Nazwa pliku",
"Device Name": "Nazwa urządzenia",
"Connect to your KOReader Sync server.": "Połącz z serwerem synchronizacji KOReader.",
@@ -492,10 +483,7 @@
"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}}%)",
@@ -610,11 +598,10 @@
"Size": "Rozmiar",
"Cover": "Okładka",
"Contain": "Zawierać",
"{{number}} pages left in chapter": "Pozostało {{number}} stron w rozdziale",
"{{number}} pages left in chapter": "<1>Pozostało </1><0>{{number}}</0><1> stron w rozdziale</1>",
"Device": "Urządzenie",
"E-Ink Mode": "Tryb E-Ink",
"Highlight Colors": "Kolory wyróżnień",
"Auto Screen Brightness": "Automatyczna jasność ekranu",
"Pagination": "Stronicowanie",
"Disable Double Tap": "Wyłącz podwójne dotknięcie",
"Tap to Paginate": "Dotknij, aby stronicować",
@@ -649,7 +636,6 @@
"Strikethrough": "Przekreślenie",
"Squiggly": "Falista linia",
"Outline": "Obrys",
"Save Current Color": "Zapisz bieżący kolor",
"Quick Colors": "Szybkie kolory",
"Highlighter": "Zakreślacz",
"Save Book Cover": "Zapisz okładkę książki",
@@ -707,9 +693,7 @@
"Validating...": "Weryfikacja...",
"View All": "Pokaż wszystkie",
"Forward": "Dalej",
"OPDS Catalog": "Katalog OPDS",
"Home": "Strona główna",
"Library": "Biblioteka",
"{{count}} items_one": "{{count}} element",
"{{count}} items_few": "{{count}} elementy",
"{{count}} items_many": "{{count}} elementów",
@@ -731,5 +715,752 @@
"Last": "Ostatnia",
"Cannot Load Page": "Nie można załadować strony",
"An error occurred": "Wystąpił błąd",
"Online Library": "Biblioteka online"
"Online Library": "Biblioteka online",
"URL must start with http:// or https://": "URL musi zaczynać się od http:// lub https://",
"Title, Author, Tag, etc...": "Tytuł, Autor, Tag, itp...",
"Query": "Zapytanie",
"Subject": "Temat",
"Enter {{terms}}": "Wprowadź {{terms}}",
"No search results found": "Nie znaleziono wyników wyszukiwania",
"Failed to load OPDS feed: {{status}} {{statusText}}": "Nie udało się załadować kanału OPDS: {{status}} {{statusText}}",
"Search in {{title}}": "Szukaj w {{title}}",
"Manage Storage": "Zarządzaj pamięcią",
"Failed to load files": "Nie udało się wczytać plików",
"Deleted {{count}} file(s)_one": "Usunięto {{count}} plik",
"Deleted {{count}} file(s)_few": "Usunięto {{count}} pliki",
"Deleted {{count}} file(s)_many": "Usunięto {{count}} plików",
"Deleted {{count}} file(s)_other": "Usunięto {{count}} pliku",
"Failed to delete {{count}} file(s)_one": "Nie udało się usunąć {{count}} pliku",
"Failed to delete {{count}} file(s)_few": "Nie udało się usunąć {{count}} plików",
"Failed to delete {{count}} file(s)_many": "Nie udało się usunąć {{count}} plików",
"Failed to delete {{count}} file(s)_other": "Nie udało się usunąć {{count}} pliku",
"Failed to delete files": "Nie udało się usunąć plików",
"Total Files": "Łączna liczba plików",
"Total Size": "Łączny rozmiar",
"Quota": "Limit",
"Used": "Użyto",
"Files": "Pliki",
"Search files...": "Szukaj plików...",
"Newest First": "Najnowsze najpierw",
"Oldest First": "Najstarsze najpierw",
"Largest First": "Największe najpierw",
"Smallest First": "Najmniejsze najpierw",
"Name A-Z": "Nazwa A-Z",
"Name Z-A": "Nazwa Z-A",
"{{count}} selected_one": "Wybrano {{count}} plik",
"{{count}} selected_few": "Wybrano {{count}} pliki",
"{{count}} selected_many": "Wybrano {{count}} plików",
"{{count}} selected_other": "Wybrano {{count}} pliku",
"Delete Selected": "Usuń wybrane",
"Created": "Utworzono",
"No files found": "Nie znaleziono plików",
"No files uploaded yet": "Nie przesłano jeszcze żadnych plików",
"files": "pliki",
"Page {{current}} of {{total}}": "Strona {{current}} z {{total}}",
"Are you sure to delete {{count}} selected file(s)?_one": "Czy na pewno chcesz usunąć {{count}} wybrany plik?",
"Are you sure to delete {{count}} selected file(s)?_few": "Czy na pewno chcesz usunąć {{count}} wybrane pliki?",
"Are you sure to delete {{count}} selected file(s)?_many": "Czy na pewno chcesz usunąć {{count}} wybranych plików?",
"Are you sure to delete {{count}} selected file(s)?_other": "Czy na pewno chcesz usunąć {{count}} wybrany plik?",
"Cloud Storage Usage": "Użycie pamięci w chmurze",
"Rename Group": "Zmień nazwę grupy",
"From Directory": "Z katalogu",
"Successfully imported {{count}} book(s)_one": "Pomyślnie zaimportowano 1 książkę",
"Successfully imported {{count}} book(s)_few": "Pomyślnie zaimportowano {{count}} książki",
"Successfully imported {{count}} book(s)_many": "Pomyślnie zaimportowano {{count}} książek",
"Successfully imported {{count}} book(s)_other": "Pomyślnie zaimportowano {{count}} książek",
"Count": "Liczba",
"Start Page": "Strona startowa",
"Search in OPDS Catalog...": "Szukaj w katalogu OPDS...",
"Please log in to use advanced TTS features": "Zaloguj się, aby korzystać z zaawansowanych funkcji TTS",
"Word limit of 30 words exceeded.": "Przekroczono limit 30 słów.",
"Proofread": "Korekta",
"Current selection": "Bieżące zaznaczenie",
"All occurrences in this book": "Wszystkie wystąpienia w tej książce",
"All occurrences in your library": "Wszystkie wystąpienia w Twojej bibliotece",
"Selected text:": "Zaznaczony tekst:",
"Replace with:": "Zastąp przez:",
"Enter text...": "Wpisz tekst…",
"Case sensitive:": "Uwzględniaj wielkość liter:",
"Scope:": "Zakres:",
"Selection": "Zaznaczenie",
"Library": "Biblioteka",
"Yes": "Tak",
"No": "Nie",
"Proofread Replacement Rules": "Reguły zastępowania korekty",
"Selected Text Rules": "Reguły dla zaznaczonego tekstu",
"No selected text replacement rules": "Brak reguł zastępowania dla zaznaczonego tekstu",
"Book Specific Rules": "Reguły specyficzne dla książki",
"No book-level replacement rules": "Brak reguł zastępowania na poziomie książki",
"Disable Quick Action": "Wyłącz szybką akcję",
"Enable Quick Action on Selection": "Włącz szybką akcję przy zaznaczeniu",
"None": "Brak",
"Annotation Tools": "Narzędzia do adnotacji",
"Enable Quick Actions": "Włącz szybkie akcje",
"Quick Action": "Szybka akcja",
"Copy to Notebook": "Kopiuj do notatnika",
"Copy text after selection": "Kopiuj zaznaczony tekst",
"Highlight text after selection": "Podświetl zaznaczony tekst",
"Annotate text after selection": "Dodaj adnotację do zaznaczonego tekstu",
"Search text after selection": "Wyszukaj zaznaczony tekst",
"Look up text in dictionary after selection": "Sprawdź zaznaczony tekst w słowniku",
"Translate text after selection": "Przetłumacz zaznaczony tekst",
"Read text aloud after selection": "Odczytaj zaznaczony tekst na głos",
"Proofread text after selection": "Sprawdź zaznaczony tekst",
"{{activeCount}} active, {{pendingCount}} pending": "{{activeCount}} aktywnych, {{pendingCount}} oczekujących",
"{{failedCount}} failed": "{{failedCount}} nieudanych",
"Waiting...": "Oczekiwanie...",
"Failed": "Nieudane",
"Completed": "Ukończone",
"Cancelled": "Anulowane",
"Retry": "Ponów",
"Active": "Aktywne",
"Transfer Queue": "Kolejka transferów",
"Upload All": "Wyślij wszystko",
"Download All": "Pobierz wszystko",
"Resume Transfers": "Wznów transfery",
"Pause Transfers": "Wstrzymaj transfery",
"Pending": "Oczekujące",
"No transfers": "Brak transferów",
"Retry All": "Ponów wszystko",
"Clear Completed": "Wyczyść ukończone",
"Clear Failed": "Wyczyść nieudane",
"Upload queued: {{title}}": "Wysyłanie w kolejce: {{title}}",
"Download queued: {{title}}": "Pobieranie w kolejce: {{title}}",
"Book not found in library": "Książka nie została znaleziona w bibliotece",
"Unknown error": "Nieznany błąd",
"Please log in to continue": "Zaloguj się, aby kontynuować",
"Cloud File Transfers": "Transfery plików w chmurze",
"Show Search Results": "Pokaż wyniki wyszukiwania",
"Search results for '{{term}}'": "Wyniki dla '{{term}}'",
"Close Search": "Zamknij wyszukiwanie",
"Previous Result": "Poprzedni wynik",
"Next Result": "Następny wynik",
"Bookmarks": "Zakładki",
"Annotations": "Adnotacje",
"Show Results": "Pokaż wyniki",
"Clear search": "Wyczyść wyszukiwanie",
"Clear search history": "Wyczyść historię wyszukiwania",
"Tap to Toggle Footer": "Dotknij, aby przełączyć stopkę",
"Show Current Time": "Pokaż aktualny czas",
"Use 24 Hour Clock": "Używaj formatu 24-godzinnego",
"Show Current Battery Status": "Pokaż aktualny stan baterii",
"Exported successfully": "Wyeksportowano pomyślnie",
"Book exported successfully.": "Książka została wyeksportowana pomyślnie.",
"Failed to export the book.": "Nie udało się wyeksportować książki.",
"Export Book": "Eksportuj książkę",
"Whole word:": "Całe słowo:",
"Date Published": "Data wydania",
"Only for TTS:": "Tylko dla TTS:",
"Uploaded": "Przesłano",
"Downloaded": "Pobrano",
"Deleted": "Usunięto",
"Note:": "Notatka:",
"Time:": "Czas:",
"Format Options": "Opcje formatu",
"Export Date": "Data eksportu",
"Chapter Titles": "Tytuły rozdziałów",
"Chapter Separator": "Separator rozdziałów",
"Highlights": "Zaznaczenia",
"Note Date": "Data notatki",
"Advanced": "Zaawansowane",
"Hide": "Ukryj",
"Show": "Pokaż",
"Use Custom Template": "Użyj własnego szablonu",
"Export Template": "Szablon eksportu",
"Template Syntax:": "Składnia szablonu:",
"Insert value": "Wstaw wartość",
"Format date (locale)": "Formatuj datę (lokalizacja)",
"Format date (custom)": "Formatuj datę (własny)",
"Conditional": "Warunkowy",
"Loop": "Pętla",
"Available Variables:": "Dostępne zmienne:",
"Book title": "Tytuł książki",
"Book author": "Autor książki",
"Export date": "Data eksportu",
"Array of chapters": "Tablica rozdziałów",
"Chapter title": "Tytuł rozdziału",
"Array of annotations": "Tablica adnotacji",
"Highlighted text": "Zaznaczony tekst",
"Annotation note": "Notatka adnotacji",
"Date Format Tokens:": "Tokeny formatu daty:",
"Year (4 digits)": "Rok (4 cyfry)",
"Month (01-12)": "Miesiąc (01-12)",
"Day (01-31)": "Dzień (01-31)",
"Hour (00-23)": "Godzina (00-23)",
"Minute (00-59)": "Minuta (00-59)",
"Second (00-59)": "Sekunda (00-59)",
"Show Source": "Pokaż źródło",
"No content to preview": "Brak treści do podglądu",
"Export": "Eksportuj",
"Set Timeout": "Ustaw limit czasu",
"Select Voice": "Wybierz głos",
"Toggle Sticky Bottom TTS Bar": "Przełącz przypiętą dolną belkę TTS",
"Display what I'm reading on Discord": "Wyświetl to, co czytam na Discord",
"Show on Discord": "Pokaż na Discord",
"Instant {{action}}": "Natychmiastowe {{action}}",
"Instant {{action}} Disabled": "Natychmiastowe {{action}} wyłączone",
"Annotation": "Adnotacja",
"Reset Template": "Resetuj szablon",
"Annotation style": "Styl adnotacji",
"Annotation color": "Kolor adnotacji",
"Annotation time": "Czas adnotacji",
"AI": "AI",
"Are you sure you want to re-index this book?": "Czy na pewno chcesz ponownie zaindeksować tę książkę?",
"Enable AI in Settings": "Włącz AI w ustawieniach",
"Index This Book": "Zaindeksuj tę książkę",
"Enable AI search and chat for this book": "Włącz wyszukiwanie AI i czat dla tej książki",
"Start Indexing": "Rozpocznij indeksowanie",
"Indexing book...": "Indeksowanie książki...",
"Preparing...": "Przygotowywanie...",
"Delete this conversation?": "Usunąć tę rozmowę?",
"No conversations yet": "Brak rozmów",
"Start a new chat to ask questions about this book": "Rozpocznij nowy czat, aby zadać pytania o tę książkę",
"Rename": "Zmień nazwę",
"New Chat": "Nowy czat",
"Chat": "Czat",
"Please enter a model ID": "Wprowadź ID modelu",
"Model not available or invalid": "Model niedostępny lub nieprawidłowy",
"Failed to validate model": "Nie udało się zwalidować modelu",
"Couldn't connect to Ollama. Is it running?": "Nie można połączyć się z Ollama. Czy jest uruchomiona?",
"Invalid API key or connection failed": "Nieprawidłowy klucz API lub błąd połączenia",
"Connection failed": "Połączenie nieudane",
"AI Assistant": "Asystent AI",
"Enable AI Assistant": "Włącz asystenta AI",
"Provider": "Dostawca",
"Ollama (Local)": "Ollama (Lokalny)",
"AI Gateway (Cloud)": "Brama AI (Chmura)",
"Ollama Configuration": "Konfiguracja Ollama",
"Refresh Models": "Odśwież modele",
"AI Model": "Model AI",
"No models detected": "Nie wykryto modeli",
"AI Gateway Configuration": "Konfiguracja bramy AI",
"Choose from a selection of high-quality, economical AI models. You can also bring your own model by selecting \"Custom Model\" below.": "Wybierz spośród wysokiej jakości, ekonomicznych modeli AI. Możesz również użyć własnego modelu, wybierając \"Model niestandardowy\" poniżej.",
"API Key": "Klucz API",
"Get Key": "Pobierz klucz",
"Model": "Model",
"Custom Model...": "Model niestandardowy...",
"Custom Model ID": "ID modelu niestandardowego",
"Validate": "Waliduj",
"Model available": "Model dostępny",
"Connection": "Połączenie",
"Test Connection": "Testuj połączenie",
"Connected": "Połączono",
"Custom Colors": "Niestandardowe kolory",
"Color E-Ink Mode": "Tryb kolorowego e-papieru",
"Reading Ruler": "Linijka do czytania",
"Enable Reading Ruler": "Włącz linijkę do czytania",
"Lines to Highlight": "Linie do wyróżnienia",
"Ruler Color": "Kolor linijki",
"Command Palette": "Paleta poleceń",
"Search settings and actions...": "Szukaj ustawień i akcji...",
"No results found for": "Brak wyników dla",
"Type to search settings and actions": "Wpisz, aby szukać ustawień i akcji",
"Recent": "Ostatnie",
"navigate": "nawiguj",
"select": "wybierz",
"close": "zamknij",
"Search Settings": "Szukaj ustawień",
"Page Margins": "Marginesy strony",
"AI Provider": "Dostawca AI",
"Ollama URL": "URL Ollama",
"Ollama Model": "Model Ollama",
"AI Gateway Model": "Model AI Gateway",
"Actions": "Akcje",
"Navigation": "Nawigacja",
"Set status for {{count}} book(s)_one": "Ustaw status dla {{count}} książki",
"Set status for {{count}} book(s)_other": "Ustaw status dla {{count}} książek",
"Mark as Unread": "Oznacz jako nieprzeczytane",
"Mark as Finished": "Oznacz jako ukończone",
"Finished": "Ukończone",
"Unread": "Nieprzeczytane",
"Clear Status": "Wyczyść status",
"Status": "Status",
"Set status for {{count}} book(s)_few": "Ustaw status dla {{count}} książek",
"Set status for {{count}} book(s)_many": "Ustaw status dla {{count}} książek",
"Loading": "Ładowanie...",
"Exit Paragraph Mode": "Wyjdź z trybu akapitu",
"Paragraph Mode": "Tryb akapitu",
"Embedding Model": "Model osadzania",
"{{count}} book(s) synced_one": "{{count}} książka zsynchronizowana",
"{{count}} book(s) synced_few": "{{count}} książki zsynchronizowane",
"{{count}} book(s) synced_many": "{{count}} książek zsynchronizowanych",
"{{count}} book(s) synced_other": "{{count}} książek zsynchronizowanych",
"Unable to start RSVP": "Nie można uruchomić RSVP",
"RSVP not supported for PDF": "RSVP nie jest obsługiwane dla PDF",
"Select Chapter": "Wybierz rozdział",
"Context": "Kontekst",
"Ready": "Gotowy",
"Chapter Progress": "Postęp rozdziału",
"words": "słowa",
"{{time}} left": "pozostało {{time}}",
"Reading progress": "Postęp czytania",
"Skip back 15 words": "Cofnij o 15 słów",
"Back 15 words (Shift+Left)": "Cofnij o 15 słów (Shift+Lewo)",
"Pause (Space)": "Pauze (Spacja)",
"Play (Space)": "Odtwarzaj (Spacja)",
"Skip forward 15 words": "Naprzód o 15 słów",
"Forward 15 words (Shift+Right)": "Naprzód o 15 słów (Shift+Prawo)",
"Decrease speed": "Zmniejsz prędkość",
"Slower (Left/Down)": "Wolniej (Lewo/Dół)",
"Increase speed": "Zwiększ prędkość",
"Faster (Right/Up)": "Szybciej (Prawo/Góra)",
"Start RSVP Reading": "Uruchom czytanie RSVP",
"Choose where to start reading": "Wybierz, gdzie zacząć czytać",
"From Chapter Start": "Od początku rozdziału",
"Start reading from the beginning of the chapter": "Zacznij czytać od początku rozdziału",
"Resume": "Wznów",
"Continue from where you left off": "Kontynuuj od miejsca, w którym przerwałeś",
"From Current Page": "Od bieżącej strony",
"Start from where you are currently reading": "Zacznij od miejsca, w którym aktualnie czytasz",
"From Selection": "Z zaznaczenia",
"Speed Reading Mode": "Tryb szybkiego czytania",
"Scroll left": "Przewiń w lewo",
"Scroll right": "Przewiń w prawo",
"Library Sync Progress": "Postęp synchronizacji biblioteki",
"Back to library": "Powrót do biblioteki",
"Group by...": "Grupuj według...",
"Export as Plain Text": "Eksportuj jako zwykły tekst",
"Export as Markdown": "Eksportuj jako Markdown",
"Show Page Navigation Buttons": "Przyciski nawigacji",
"Page {{number}}": "Strona {{number}}",
"highlight": "wyróżnienie",
"underline": "podkreślenie",
"squiggly": "wężyk",
"red": "czerwony",
"violet": "fioletowy",
"blue": "niebieski",
"green": "zielony",
"yellow": "żółty",
"Select {{style}} style": "Wybierz styl {{style}}",
"Select {{color}} color": "Wybierz kolor {{color}}",
"Close Book": "Zamknij książkę",
"Speed Reading": "Szybkie czytanie",
"Close Speed Reading": "Zamknij szybkie czytanie",
"Authors": "Autorzy",
"Books": "Książki",
"Groups": "Grupy",
"Back to TTS Location": "Powrót do lokalizacji TTS",
"Metadata": "Metadane",
"Image viewer": "Przeglądarka obrazów",
"Previous Image": "Poprzedni obraz",
"Next Image": "Następny obraz",
"Zoomed": "Powiększone",
"Zoom level": "Poziom powiększenia",
"Table viewer": "Przeglądarka tabel",
"Unable to connect to Readwise. Please check your network connection.": "Nie można połączyć się z Readwise. Sprawdź połączenie sieciowe.",
"Invalid Readwise access token": "Nieprawidłowy token dostępu Readwise",
"Disconnected from Readwise": "Rozłączono z Readwise",
"Never": "Nigdy",
"Sync Enabled": "Synchronizacja włączona",
"Disconnect": "Rozłącz",
"Connect your Readwise account to sync highlights.": "Połącz swoje konto Readwise, aby synchronizować wyróżnienia.",
"Get your access token at": "Pobierz token dostępu pod adresem",
"Access Token": "Token dostępu",
"Paste your Readwise access token": "Wklej swój token dostępu Readwise",
"Readwise Sync": "Synchronizacja Readwise",
"Push Highlights": "Wyślij wyróżnienia",
"Highlights synced to Readwise": "Wyróżnienia zsynchronizowane z Readwise",
"Readwise sync failed: no internet connection": "Synchronizacja Readwise nie powiodła się: brak połączenia z Internetem",
"Readwise sync failed: {{error}}": "Synchronizacja Readwise nie powiodła się: {{error}}",
"System Screen Brightness": "Jasność ekranu systemowego",
"Page:": "Strona:",
"Page: {{number}}": "Strona: {{number}}",
"Annotation page number": "Numer strony adnotacji",
"Translating...": "Tłumaczenie...",
"Show Battery Percentage": "Pokaż procent baterii",
"Hide Scrollbar": "Ukryj pasek przewijania",
"Skip to last reading position": "Przejdź do ostatniej pozycji czytania",
"Show context": "Pokaż kontekst",
"Hide context": "Ukryj kontekst",
"Decrease font size": "Zmniejsz rozmiar czcionki",
"Increase font size": "Zwiększ rozmiar czcionki",
"Focus": "Fokus",
"Theme color": "Kolor motywu",
"Import failed": "Import nie powiódł się",
"Available Formatters:": "Dostępne formatery:",
"Format date": "Formatuj datę",
"Markdown block quote (> per line)": "Cytat blokowy Markdown (> na linię)",
"Newlines to <br>": "Nowe linie na <br>",
"Change case": "Zmień wielkość liter",
"Trim whitespace": "Przytnij spacje",
"Truncate to n characters": "Skróć do n znaków",
"Replace text": "Zamień tekst",
"Fallback value": "Wartość zastępcza",
"Get length": "Pobierz długość",
"First/last element": "Pierwszy/ostatni element",
"Join array": "Połącz tablicę",
"Backup failed: {{error}}": "Kopia zapasowa nie powiodła się: {{error}}",
"Select Backup": "Wybierz kopię zapasową",
"Restore failed: {{error}}": "Przywracanie nie powiodło się: {{error}}",
"Backup & Restore": "Kopia zapasowa i przywracanie",
"Create a backup of your library or restore from a previous backup. Restoring will merge with your current library.": "Utwórz kopię zapasową biblioteki lub przywróć z poprzedniej kopii. Przywracanie zostanie połączone z bieżącą biblioteką.",
"Backup Library": "Kopia zapasowa biblioteki",
"Restore Library": "Przywróć bibliotekę",
"Creating backup...": "Tworzenie kopii zapasowej...",
"Restoring library...": "Przywracanie biblioteki...",
"{{current}} of {{total}} items": "{{current}} z {{total}} elementów",
"Backup completed successfully!": "Kopia zapasowa ukończona!",
"Restore completed successfully!": "Przywracanie ukończone!",
"Your library has been saved to the selected location.": "Biblioteka została zapisana w wybranej lokalizacji.",
"{{added}} books added, {{updated}} books updated.": "Dodano {{added}} książek, zaktualizowano {{updated}} książek.",
"Operation failed": "Operacja nie powiodła się",
"Loading library...": "Wczytywanie biblioteki...",
"{{count}} books refreshed_one": "Odświeżono {{count}} książkę",
"{{count}} books refreshed_few": "Odświeżono {{count}} książki",
"{{count}} books refreshed_many": "Odświeżono {{count}} książek",
"{{count}} books refreshed_other": "Odświeżono {{count}} książek",
"Failed to refresh metadata": "Nie udało się odświeżyć metadanych",
"Refresh Metadata": "Odśwież metadane",
"Keyboard Shortcuts": "Skróty klawiszowe",
"View all keyboard shortcuts": "Zobacz wszystkie skróty klawiszowe",
"Switch Sidebar Tab": "Przełącz kartę paska bocznego",
"Toggle Notebook": "Pokaż/ukryj notatnik",
"Search in Book": "Szukaj w książce",
"Toggle Scroll Mode": "Przełącz tryb przewijania",
"Toggle Select Mode": "Przełącz tryb zaznaczania",
"Toggle Bookmark": "Przełącz zakładkę",
"Toggle Text to Speech": "Przełącz tekst na mowę",
"Play / Pause TTS": "Odtwórz / Wstrzymaj TTS",
"Toggle Paragraph Mode": "Przełącz tryb akapitu",
"Highlight Selection": "Podświetl zaznaczenie",
"Underline Selection": "Podkreśl zaznaczenie",
"Annotate Selection": "Dodaj adnotację do zaznaczenia",
"Search Selection": "Wyszukaj zaznaczenie",
"Copy Selection": "Kopiuj zaznaczenie",
"Translate Selection": "Przetłumacz zaznaczenie",
"Dictionary Lookup": "Wyszukaj w słowniku",
"Read Aloud Selection": "Odczytaj zaznaczenie na głos",
"Proofread Selection": "Sprawdź zaznaczenie",
"Open Settings": "Otwórz ustawienia",
"Open Command Palette": "Otwórz paletę poleceń",
"Show Keyboard Shortcuts": "Pokaż skróty klawiszowe",
"Open Books": "Otwórz książki",
"Toggle Fullscreen": "Przełącz pełny ekran",
"Close Window": "Zamknij okno",
"Quit App": "Zamknij aplikację",
"Go Left / Previous Page": "W lewo / Poprzednia strona",
"Go Right / Next Page": "W prawo / Następna strona",
"Go Up": "W górę",
"Go Down": "W dół",
"Previous Chapter": "Poprzedni rozdział",
"Next Chapter": "Następny rozdział",
"Scroll Half Page Down": "Przewiń o pół strony w dół",
"Scroll Half Page Up": "Przewiń o pół strony w górę",
"Save Note": "Zapisz notatkę",
"Single Section Scroll": "Przewijanie pojedynczej sekcji",
"General": "Ogólne",
"Text to Speech": "Tekst na mowę",
"Zoom": "Powiększenie",
"Window": "Okno",
"Your Bookshelf": "Twoja półka z książkami",
"TTS": "Czytanie na głos",
"Media Info": "Informacje o mediach",
"Update Frequency": "Częstotliwość aktualizacji",
"Every Sentence": "Co zdanie",
"Every Paragraph": "Co akapit",
"Every Chapter": "Co rozdział",
"TTS Media Info Update Frequency": "Częstotliwość aktualizacji informacji o mediach TTS",
"Select reading speed": "Wybierz prędkość czytania",
"Drag to seek": "Przeciągnij, aby przewinąć",
"Punctuation Delay": "Opóźnienie interpunkcji",
"Please confirm that this OPDS connection will be proxied through Readest servers on the web app before continuing.": "Potwierdź, że to połączenie OPDS będzie przekierowywane przez serwery Readest w aplikacji webowej, zanim przejdziesz dalej.",
"Custom Headers (optional)": "Niestandardowe nagłówki (opcjonalnie)",
"Add one header per line using \"Header-Name: value\".": "Dodaj jeden nagłówek w każdym wierszu, używając formatu \"Header-Name: value\".",
"I understand this OPDS connection will be proxied through Readest servers on the web app. If I do not trust Readest with these credentials or headers, I should use the native app instead.": "Rozumiem, że to połączenie OPDS będzie przekierowywane przez serwery Readest w aplikacji webowej. Jeśli nie ufam Readest w kwestii tych danych uwierzytelniających lub nagłówków, powinienem użyć aplikacji natywnej.",
"Unable to connect to Hardcover. Please check your network connection.": "Nie można połączyć się z Hardcover. Sprawdź połączenie sieciowe.",
"Invalid Hardcover API token": "Nieprawidłowy token API Hardcover",
"Disconnected from Hardcover": "Rozłączono z Hardcover",
"Connect your Hardcover account to sync reading progress and notes.": "Połącz swoje konto Hardcover, aby synchronizować postęp czytania i notatki.",
"Get your API token from hardcover.app → Settings → API.": "Pobierz token API z hardcover.app → Ustawienia → API.",
"API Token": "Token API",
"Paste your Hardcover API token": "Wklej swój token API Hardcover",
"Hardcover Sync": "Synchronizacja Hardcover",
"Push Notes": "Wyślij notatki",
"No annotations or excerpts to sync for this book.": "Brak adnotacji lub fragmentów do zsynchronizowania dla tej książki.",
"Configure Hardcover in Settings first.": "Najpierw skonfiguruj Hardcover w ustawieniach.",
"No new Hardcover note changes to sync.": "Brak nowych zmian notatek Hardcover do zsynchronizowania.",
"Hardcover synced: {{inserted}} new, {{updated}} updated, {{skipped}} unchanged": "Hardcover zsynchronizowano: {{inserted}} nowych, {{updated}} zaktualizowanych, {{skipped}} bez zmian",
"Hardcover notes sync failed: {{error}}": "Synchronizacja notatek Hardcover nie powiodła się: {{error}}",
"Reading progress synced to Hardcover": "Postęp czytania zsynchronizowany z Hardcover",
"Hardcover progress sync failed: {{error}}": "Synchronizacja postępu Hardcover nie powiodła się: {{error}}",
"ISBN": "ISBN",
"Keep existing source identifier": "Zachowaj istniejący identyfikator źródła",
"Toggle Toolbar": "Przełącz pasek narzędzi",
"Split Hyphens": "Dziel łączniki",
"Apply Theme Colors to PDF": "Zastosuj kolory motywu do PDF",
"Name": "Nazwa",
"Remove": "Usuń",
"Edit OPDS Catalog": "Edytuj katalog OPDS",
"Save Changes": "Zapisz zmiany",
"Use Book Layout": "Użyj układu książki",
"End of this section. Continue to the next.": "Koniec tej sekcji. Przejdź do następnej.",
"Auto-download": "Automatyczne pobieranie",
"Auto-download new items": "Automatycznie pobieraj nowe pozycje",
"Automatically download new publications when the app syncs": "Automatycznie pobieraj nowe publikacje podczas synchronizacji aplikacji",
"{{count}} new item(s) downloaded from OPDS_one": "Pobrano {{count}} nową pozycję z OPDS",
"{{count}} new item(s) downloaded from OPDS_few": "Pobrano {{count}} nowe pozycje z OPDS",
"{{count}} new item(s) downloaded from OPDS_many": "Pobrano {{count}} nowych pozycji z OPDS",
"{{count}} new item(s) downloaded from OPDS_other": "Pobrano {{count}} nowych pozycji z OPDS",
"Failed to sync {{count}} OPDS catalog(s)_one": "Nie udało się zsynchronizować {{count}} katalogu OPDS",
"Failed to sync {{count}} OPDS catalog(s)_few": "Nie udało się zsynchronizować {{count}} katalogów OPDS",
"Failed to sync {{count}} OPDS catalog(s)_many": "Nie udało się zsynchronizować {{count}} katalogów OPDS",
"Failed to sync {{count}} OPDS catalog(s)_other": "Nie udało się zsynchronizować {{count}} katalogów OPDS",
"Last synced {{when}}": "Ostatnia synchronizacja {{when}}",
"Failed downloads": "Nieudane pobrania",
"No failed downloads": "Brak nieudanych pobrań",
"Attempts: {{count}}_one": "Próby: {{count}}",
"Attempts: {{count}}_few": "Próby: {{count}}",
"Attempts: {{count}}_many": "Próby: {{count}}",
"Attempts: {{count}}_other": "Próby: {{count}}",
"Skip": "Pomiń",
"Skip all": "Pomiń wszystko",
"Retry all": "Ponów wszystkie",
"{{count}} failed_one": "{{count}} nieudane",
"{{count}} failed_few": "{{count}} nieudane",
"{{count}} failed_many": "{{count}} nieudanych",
"{{count}} failed_other": "{{count}} nieudanych",
"(none)": "(brak)",
"Identifiers": "Identyfikatory",
"Read (Stream)": "Czytaj (Strumień)",
"Failed to start stream": "Nie udało się uruchomić strumienia",
"No dictionaries enabled": "Brak włączonych słowników",
"Enable a dictionary in Settings → Language → Dictionaries.": "Włącz słownik w Ustawienia → Język → Słowniki.",
"Wiktionary": "Wikisłownik",
"Drag to reorder": "Przeciągnij, aby zmienić kolejność",
"Built-in": "Wbudowany",
"Bundle is missing on this device. Re-import to use it.": "Brak pakietu na tym urządzeniu. Zaimportuj ponownie, aby użyć.",
"This dictionary format is not supported.": "Ten format słownika nie jest obsługiwany.",
"MDict": "MDict",
"StarDict": "StarDict",
"Imported {{count}} dictionary_one": "Zaimportowano {{count}} słownik",
"Imported {{count}} dictionary_few": "Zaimportowano {{count}} słowniki",
"Imported {{count}} dictionary_many": "Zaimportowano {{count}} słowników",
"Imported {{count}} dictionary_other": "Zaimportowano {{count}} słowników",
"Skipped incomplete bundles: {{names}}": "Pominięto niekompletne pakiety: {{names}}",
"Failed to import dictionary: {{message}}": "Nie udało się zaimportować słownika: {{message}}",
"Dictionaries": "Słowniki",
"Delete Dictionary": "Usuń słownik",
"Importing…": "Importowanie…",
"Import Dictionary": "Importuj słownik",
"No dictionaries available.": "Brak dostępnych słowników.",
"StarDict bundles need .ifo, .idx, and .dict.dz files (.syn optional).": "Pakiety StarDict wymagają plików .ifo, .idx i .dict.dz (.syn opcjonalny).",
"Select all the bundle files together when importing.": "Podczas importu wybierz wszystkie pliki pakietu jednocześnie.",
"Manage Dictionaries": "Zarządzaj słownikami",
"Select Dictionary Files": "Wybierz pliki słownika",
"Read on Wikipedia →": "Czytaj na Wikipedii →",
"This link can't be opened": "Nie można otworzyć tego linku",
"The annotation link is missing required information. The original link may have been truncated.": "W linku do adnotacji brakuje wymaganych informacji. Pierwotny link mógł zostać obcięty.",
"Go to Readest": "Przejdź do Readest",
"Open-source ebook reader for everyone, on every device.": "Otwartoźródłowy czytnik e-booków dla każdego, na każdym urządzeniu.",
"Open in Readest": "Otwórz w Readest",
"If Readest didn't open automatically, choose an option below:": "Jeśli Readest nie otworzył się automatycznie, wybierz opcję poniżej:",
"Continue reading where you left off.": "Kontynuuj czytanie od miejsca, w którym skończyłeś.",
"Readest logo": "Logo Readest",
"Opening Readest...": "Otwieranie Readest…",
"Open in Readest app": "Otwórz w aplikacji Readest",
"Continue in browser": "Kontynuuj w przeglądarce",
"Don't have Readest?": "Nie masz jeszcze Readest?",
"Book not in your library": "Książki nie ma w Twojej bibliotece",
"Share Book": "Udostępnij książkę",
"Could not create share link": "Nie można utworzyć linku udostępniania",
"Link copied": "Link skopiowany",
"Could not copy link": "Nie można skopiować linku",
"Share revoked": "Udostępnianie odwołane",
"Could not revoke share": "Nie można odwołać udostępniania",
"Uploading book…": "Przesyłanie książki…",
"Generating…": "Generowanie…",
"Generate share link": "Utwórz link udostępniania",
"Share URL": "Adres udostępniania",
"Copy link": "Kopiuj link",
"Copied": "Skopiowano",
"Share via…": "Udostępnij przez…",
"Expires {{date}}": "Wygasa {{date}}",
"Revoking…": "Odwoływanie…",
"Revoke share": "Odwołaj udostępnianie",
"Sign in to share books": "Zaloguj się, aby udostępniać książki",
"Expiring soon": "Wkrótce wygaśnie",
"Missing share token": "Brakuje tokenu udostępniania",
"Could not add to your library": "Nie można dodać do biblioteki",
"This share link is no longer available": "Ten link udostępniania jest już niedostępny",
"The original link may have expired or been revoked.": "Pierwotny link mógł wygasnąć lub został odwołany.",
"Get Readest": "Pobierz Readest",
"Loading shared book…": "Wczytywanie udostępnionej książki…",
"Adding…": "Dodawanie…",
"Add to my library": "Dodaj do mojej biblioteki",
"Could not load your shares": "Nie można wczytać udostępnień",
"Revoked": "Odwołany",
"Expired": "Wygasł",
"Shared books": "Udostępnione książki",
"You haven't shared any books yet": "Nie udostępniłeś jeszcze żadnych książek",
"Open a book and tap Share to send it to a friend.": "Otwórz książkę i dotknij Udostępnij, aby wysłać ją znajomemu.",
"{{count}} active_one": "{{count}} aktywny",
"{{count}} active_few": "{{count}} aktywne",
"{{count}} active_many": "{{count}} aktywnych",
"{{count}} active_other": "{{count}} aktywnych",
"{{count}} downloads_one": "{{count}} pobranie",
"{{count}} downloads_few": "{{count}} pobrania",
"{{count}} downloads_many": "{{count}} pobrań",
"{{count}} downloads_other": "{{count}} pobrań",
"starts at saved page": "zaczyna się od zapisanej strony",
"More actions": "Więcej akcji",
"Loading…": "Wczytywanie…",
"Load more": "Wczytaj więcej",
"Could not load shared book": "Nie można wczytać udostępnionej książki",
"The share link is missing required information.": "W linku udostępniania brakuje wymaganych informacji.",
"Please check your connection and try again.": "Sprawdź połączenie i spróbuj ponownie.",
"Sign in to import shared books": "Zaloguj się, aby importować udostępnione książki",
"Already in your library": "Już w Twojej bibliotece",
"Added to your library": "Dodano do biblioteki",
"Could not import shared book": "Nie można zaimportować udostępnionej książki",
"Manage Shared Links": "Zarządzaj linkami udostępniania",
"Expires in": "Wygasa za",
"{{count}} days_one": "1 dzień",
"{{count}} days_few": "{{count}} dni",
"{{count}} days_many": "{{count}} dni",
"{{count}} days_other": "{{count}} dni",
"Expires in {{count}} days_one": "Wygasa za 1 dzień",
"Expires in {{count}} days_few": "Wygasa za {{count}} dni",
"Expires in {{count}} days_many": "Wygasa za {{count}} dni",
"Expires in {{count}} days_other": "Wygasa za {{count}} dni",
"Expires in {{count}} hours_one": "Wygasa za 1 godz.",
"Expires in {{count}} hours_few": "Wygasa za {{count}} godz.",
"Expires in {{count}} hours_many": "Wygasa za {{count}} godz.",
"Expires in {{count}} hours_other": "Wygasa za {{count}} godz.",
"Open in app": "Otwórz w aplikacji",
"Shared with you": "Udostępnione Tobie",
"Downloading… {{percent}}%": "Pobieranie… {{percent}}%",
"Import progress": "Postęp importu",
"Share reading progress": "Udostępnij postęp czytania",
"Includes your reading progress": "Zawiera Twój postęp czytania",
"Synced {{time}}": "Zsynchronizowano {{time}}",
"Sync Info": "Informacje o synchronizacji",
"Last Synced": "Ostatnia synchronizacja",
"DICT": "DICT",
"Slob": "Slob",
"DICT bundles need a .index file and a .dict.dz file.": "Pakiety DICT wymagają pliku .index i pliku .dict.dz.",
"Slob bundles need a .slob file.": "Pakiety Slob wymagają pliku .slob.",
"URL template must start with http(s):// and contain %WORD%.": "Szablon URL musi zaczynać się od http(s):// i zawierać %WORD%.",
"Web": "Web",
"Add Web Search": "Dodaj wyszukiwanie internetowe",
"Edit Web Search": "Edytuj wyszukiwanie internetowe",
"e.g. Google": "np. Google",
"URL Template": "Szablon URL",
"Use %WORD% where the looked-up word should appear.": "Użyj %WORD% w miejscu, w którym ma się pojawić wyszukiwane słowo.",
"Open the search result in your browser:": "Otwórz wynik wyszukiwania w przeglądarce:",
"Open in {{name}}": "Otwórz w {{name}}",
"Book Fingerprint": "Odcisk książki",
"Search the web": "Szukaj w sieci",
"App deeplink (readest://)": "Głęboki link aplikacji (readest://)",
"Universal web link (https://)": "Uniwersalny link sieciowy (https://)",
"Name cannot be empty.": "Nazwa nie może być pusta.",
"Replaced {{count}} existing dictionary_one": "Zastąpiono {{count}} istniejący słownik",
"Replaced {{count}} existing dictionary_few": "Zastąpiono {{count}} istniejące słowniki",
"Replaced {{count}} existing dictionary_many": "Zastąpiono {{count}} istniejących słowników",
"Replaced {{count}} existing dictionary_other": "Zastąpiono {{count}} istniejące słowniki",
"Cancel Edit": "Anuluj edycję",
"Edit Dictionary": "Edytuj słownik",
"MDict bundles use .mdx files; companion .mdd and .css files are optional.": "Pakiety MDict używają plików .mdx; towarzyszące pliki .mdd i .css są opcjonalne.",
"Dictionary name": "Nazwa słownika",
"This audio can't play here — the dictionary uses an outdated format. Try one with Opus, MP3, or WAV audio.": "Tego dźwięku nie można odtworzyć tutaj — słownik używa przestarzałego formatu. Spróbuj słownika z dźwiękiem Opus, MP3 lub WAV.",
"File uploaded: {{title}}": "Plik przesłany: {{title}}",
"File downloaded: {{title}}": "Plik pobrany: {{title}}",
"Deleted cloud copy of the file: {{title}}": "Usunięto kopię pliku w chmurze: {{title}}",
"Failed to upload file: {{title}}": "Nie udało się przesłać pliku: {{title}}",
"Failed to download file: {{title}}": "Nie udało się pobrać pliku: {{title}}",
"Failed to delete cloud copy of the file: {{title}}": "Nie udało się usunąć kopii pliku w chmurze: {{title}}",
"{{percentage}}% used": "Wykorzystano {{percentage}}%",
"Set PIN…": "Ustaw PIN…",
"Require a 4-digit PIN to open Readest": "Wymagaj 4-cyfrowego PIN-u do otwarcia Readest",
"Change PIN…": "Zmień PIN…",
"Disable PIN…": "Wyłącz PIN…",
"Sync passphrase ready": "Hasło synchronizacji gotowe",
"This permanently deletes the encrypted credentials we sync (e.g., OPDS catalog passwords) on every device. Local copies are preserved. You will need to re-enter the sync passphrase or set a new one. Continue?": "Spowoduje to trwałe usunięcie zaszyfrowanych danych uwierzytelniających, które synchronizujemy (np. haseł katalogu OPDS), na każdym urządzeniu. Lokalne kopie zostaną zachowane. Konieczne będzie ponowne wprowadzenie hasła synchronizacji lub ustawienie nowego. Kontynuować?",
"Sync passphrase forgotten — all encrypted fields cleared": "Zapomniano hasła synchronizacji — wszystkie zaszyfrowane pola wyczyszczone",
"Sync passphrase": "Hasło synchronizacji",
"Set passphrase": "Ustaw hasło",
"Forgot passphrase": "Zapomniałem hasła",
"Incorrect PIN": "Nieprawidłowy PIN",
"App locked": "Aplikacja zablokowana",
"Enter your PIN": "Wprowadź swój PIN",
"Readest is locked. Enter your 4-digit PIN to continue.": "Readest jest zablokowany. Wprowadź swój 4-cyfrowy PIN, aby kontynuować.",
"PIN code": "Kod PIN",
"Forgetting your PIN locks you out of this device. You'll need to clear the app's data to reset it.": "Zapomnienie PIN-u zablokuje Cię na tym urządzeniu. Aby go zresetować, musisz wyczyścić dane aplikacji.",
"Passphrase must be at least 8 characters": "Hasło musi mieć co najmniej 8 znaków",
"Passphrases do not match": "Hasła nie pasują do siebie",
"Set sync passphrase": "Ustaw hasło synchronizacji",
"Enter sync passphrase": "Wprowadź hasło synchronizacji",
"A sync passphrase encrypts your sensitive fields (like OPDS catalog credentials) before they sync. We never see this passphrase. Pick something memorable — there is no recovery without it.": "Hasło synchronizacji szyfruje Twoje wrażliwe pola (np. dane uwierzytelniające katalogu OPDS) przed synchronizacją. Nigdy nie widzimy tego hasła. Wybierz coś łatwego do zapamiętania — bez niego nie ma odzyskiwania.",
"Enter the sync passphrase you set on another device to decrypt your synced credentials.": "Wprowadź hasło synchronizacji ustawione na innym urządzeniu, aby odszyfrować zsynchronizowane dane uwierzytelniające.",
"Confirm passphrase": "Potwierdź hasło",
"Unlock": "Odblokuj",
"Set PIN": "Ustaw PIN",
"Change PIN": "Zmień PIN",
"Disable PIN": "Wyłącz PIN",
"Enter your current PIN, then choose a new 4-digit PIN.": "Wprowadź swój obecny PIN, a następnie wybierz nowy 4-cyfrowy PIN.",
"Enter your current PIN to disable the app lock.": "Wprowadź swój obecny PIN, aby wyłączyć blokadę aplikacji.",
"PIN must be {{length}} digits": "PIN musi mieć {{length}} cyfr",
"PINs do not match": "PIN-y nie pasują do siebie",
"Current PIN": "Obecny PIN",
"New PIN": "Nowy PIN",
"Confirm new PIN": "Potwierdź nowy PIN",
"Manage Sync": "Zarządzaj synchronizacją",
"Imported book files and library metadata": "Zaimportowane pliki książek i metadane biblioteki",
"Last-read position, bookmarks, and per-book preferences": "Ostatnia pozycja czytania, zakładki i ustawienia poszczególnych książek",
"Highlights and notes": "Podświetlenia i notatki",
"Fonts": "Czcionki",
"Custom font files": "Niestandardowe pliki czcionek",
"Backgrounds": "Tła",
"Custom background textures": "Niestandardowe tekstury tła",
"OPDS catalogs": "Katalogi OPDS",
"Saved catalog URLs and (encrypted) credentials": "Zapisane adresy URL katalogów i (zaszyfrowane) dane uwierzytelniające",
"Wrong sync passphrase — synced credentials could not be decrypted": "Nieprawidłowe hasło synchronizacji — nie można odszyfrować zsynchronizowanych danych uwierzytelniających",
"Sync passphrase data on the server was reset. Re-encrypting your credentials under the new passphrase…": "Dane hasła synchronizacji na serwerze zostały zresetowane. Ponowne szyfrowanie danych uwierzytelniających przy użyciu nowego hasła…",
"Failed to decrypt synced credentials": "Nie udało się odszyfrować zsynchronizowanych danych uwierzytelniających",
"Imported dictionary bundles and settings": "Zaimportowane pakiety słowników i ustawienia",
"Choose what syncs across your devices. Disabling a category stops this device from sending or receiving rows of that kind. Anything already on the server is left alone, re-enabling resumes from where you stopped.": "Wybierz, co ma być synchronizowane między Twoimi urządzeniami. Wyłączenie kategorii sprawia, że to urządzenie przestaje wysyłać i odbierać wpisy tego typu. To, co jest już na serwerze, pozostaje nietknięte, a ponowne włączenie wznawia synchronizację od miejsca, w którym ją zatrzymałeś.",
"Data Sync": "Synchronizacja danych",
"Manage Fonts": "Zarządzaj czcionkami",
"App settings": "Ustawienia aplikacji",
"Theme, highlight colours, integrations (KOSync, Readwise, Hardcover), and dictionary order": "Motyw, kolory podświetleń, integracje (KOSync, Readwise, Hardcover) oraz kolejność słowników",
"Required while Dictionaries sync is enabled": "Wymagane, gdy włączona jest synchronizacja Słowników",
"Resets in {{hours}} hr {{minutes}} min": "Resetuje się za {{hours}} godz. {{minutes}} min",
"Pick a 4-digit PIN. You will need to enter it every time you open Readest. There is no way to recover a forgotten PIN. Clearing the app data is the only way to reset it.": "Wybierz 4-cyfrowy PIN. Będziesz musiał go wprowadzać za każdym razem, gdy otworzysz Readest. Nie ma sposobu na odzyskanie zapomnianego PIN-u. Wyczyszczenie danych aplikacji to jedyny sposób, aby go zresetować.",
"Credentials": "Poświadczenia",
"Tokens, usernames, and passwords for OPDS, KOReader, Hardcover, and Readwise. When disabled, credentials remain on this device only and are never uploaded.": "Tokeny, nazwy użytkowników i hasła do OPDS, KOReader, Hardcover i Readwise. Gdy wyłączone, poświadczenia pozostają tylko na tym urządzeniu i nigdy nie są przesyłane.",
"Sensitive synced fields are encrypted before upload. Set a passphrase now or later when encryption is needed.": "Wrażliwe zsynchronizowane pola są szyfrowane przed wysłaniem. Ustaw hasło teraz lub później, gdy szyfrowanie będzie potrzebne.",
"Saved to this account. You will be prompted for it when decrypting credentials.": "Zapisane w tym koncie. Zostaniesz o nie poproszony przy odszyfrowywaniu poświadczeń.",
"Reading progress on this device differs from \"{{deviceName}}\".": "Postęp czytania na tym urządzeniu różni się od „{{deviceName}}”.",
"This device": "To urządzenie",
"Red": "Czerwony",
"Yellow": "Żółty",
"Green": "Zielony",
"Blue": "Niebieski",
"Violet": "Fioletowy",
"Edit color": "Edytuj kolor",
"Add custom color": "Dodaj kolor niestandardowy",
"Add label": "Dodaj etykietę",
"Choose color": "Wybierz kolor",
"Connected to Hardcover. Last synced {{time}}.": "Połączono z Hardcover. Ostatnia synchronizacja {{time}}.",
"Integrations": "Integracje",
"Hardcover": "Hardcover",
"Connected to Readwise. Last synced {{time}}.": "Połączono z Readwise. Ostatnia synchronizacja {{time}}.",
"Readwise": "Readwise",
"Browse and download books from online catalogs.": "Przeglądaj i pobieraj książki z katalogów online.",
"Connected as {{user}}": "Połączono jako {{user}}",
"Not connected": "Nie połączono",
"{{count}} catalog_one": "{{count}} katalog",
"{{count}} catalog_few": "{{count}} katalogi",
"{{count}} catalog_many": "{{count}} katalogów",
"{{count}} catalog_other": "{{count}} katalogu",
"No catalogs": "Brak katalogów",
"Connect Readest to external services for sync, highlights, and catalogs.": "Połącz Readest z usługami zewnętrznymi w celu synchronizacji, podświetleń i katalogów.",
"Reading Sync": "Synchronizacja czytania",
"Content Sources": "Źródła treści",
"Scroll tabs": "Przewiń karty",
"Catalog actions": "Akcje katalogu",
"File Content": "Zawartość pliku",
"Start your library": "Rozpocznij swoją bibliotekę",
"Pick a book from your device to add it to your library.": "Wybierz książkę z urządzenia, aby dodać ją do swojej biblioteki.",
"Drop a book anywhere on this window, or pick one from your computer.": "Upuść książkę w dowolnym miejscu tego okna lub wybierz ją z komputera.",
"Sign in to sync your library": "Zaloguj się, aby zsynchronizować bibliotekę"
}
File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More