chore(i18n): translate new strings; commit pending agent memory notes (#4430)
Translate 4 new keys across all 33 locales: - Send - Book file is not available locally - Failed to send book - Highlight Current Sentence Also commit pending .claude/memory bug-fix notes. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -12,9 +12,11 @@
|
||||
- [Issue #4112 scroll-anchoring](issue-4112-scroll-anchoring.md) — RESOLVED (PR #4349). Scroll-anchoring suppressed at scrollTop 0 when prepending a section in scrolled mode; fix patterns (prepend compensation, eager backward preload, no-blank nav) + test & dev-server gotchas
|
||||
- [Reading ruler line/column-aware](reading-ruler-line-aware.md) — ruler snaps to real lines; multi-column band spans one column; Range.getClientRects() returns tall block boxes that must be dropped; iframe frame-offset mapping; synthetic-key throttling
|
||||
- [TOC expand + auto-scroll](toc-expand-and-autoscroll.md) — #4059 collapse-by-default policy in `tocTree.ts`; pinned-sidebar mounts before progress → dynamic expansion breaks scroll-to-current via (1) spurious onScroll clearing pending and (2) Virtuoso scrollToIndex landing short after row growth (re-assert on rAF)
|
||||
- [BooknoteView auto-scroll (#4352)](booknote-view-autoscroll-4352.md) — virtualizing the annotation/bookmark list dropped auto-scroll-to-nearest; two paths (reload: OverlayScrollbars resets scrollTop → re-apply in `initialized` via ref; tab-switch: synchronous scrollToIndex on fresh-mounted list wedges Virtuoso → use `initialTopMostItemIndex` + skip-gate). Mirrors TOCView. Includes dev-server/Fast-Refresh/screenshot-vs-DOM verification gotchas
|
||||
- [Swipe page-turn bg flash](paginator-swipe-bg-flash.md) — white↔black flash on swipe+animation only; `#background` was static screen-space and didn't track content during drag/snap; fix = sliding per-view full-bleed segments (`computeBackgroundSegments`) rebuilt on scroll + per-rAF synced to the view transform during snap
|
||||
- [Duokan fullscreen cover hidden in scroll mode](duokan-fullscreen-cover-scroll.md) — #4379 `data-duokan-page-fullscreen` cover pinned `position:absolute height:100%` collapses against auto-height scroll container; gate fullscreen on `this.#column` + reset stale absolute props on toggle (`setImageSize` in paginator.js)
|
||||
- [Paginated texture occlusion](paginated-texture-occlusion-4399.md) — #4399 host `.foliate-viewer::before` texture absent in paginated (shown in scrolled); opaque `#background` container (`= fallbackBg`) from the swipe-flash fix occludes it; shared `textureAwareBackground` helper + `hasTexture ? '' : fallbackBg` container
|
||||
- [Background overflows column (#4394, PR #4429)](paginator-gutter-bleed-asymmetry-4394.md) — paginated page bg stretched into the outer `--_outer-min` gutter → mixed cover/title 2-up spread shifted off-centre (~250px at 1920px). KEEP the grid (`--_outer-min` keeps margins symmetric); fix = clamp `computeBackgroundSegments` to `[containerStart,containerEnd]` (Math.max/Math.min) so bg stays in its column. 2 wrong tries first (bleed-gating, "page shouldn't be yellow"); foliate submodule needs dev-server RESTART to pick up edits
|
||||
|
||||
## Critical Files (Most Bug-Prone)
|
||||
- `src/utils/style.ts` - Central EPUB CSS transformation hub (14+ bug fixes)
|
||||
@@ -50,6 +52,15 @@
|
||||
- [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
|
||||
|
||||
## Reader UI Fixes
|
||||
- [Android image callout freeze](android-image-callout-freeze.md) — long-press `<img>` fires WebView native callout that collides with app touch handlers → whole-app freeze; `-webkit-touch-callout: none` doesn't inherit so put `.no-context-menu` on an ancestor of the image (`.no-context-menu img` rule in globals.css); seen on book covers (#4345) + image preview/zoom (#4420, `ImageViewer.tsx`)
|
||||
- [ProgressBar focus-ring line (#4397)](progressbar-focus-ring-4397.md) — decorative `.progressinfo` footer was `tabIndex={-1}` → Android long-press focused it → stray content-width focus-ring line at the bottom every page; fix = drop tabIndex (role='presentation' must not be focusable); ffmpeg-the-video debugging + live-browser `:focus-visible` confirmation
|
||||
- [Table dark-mode tint regression (#4419)](table-dark-mode-tint-4419.md) — `blockquote, table *` color-mix tint in `getColorStyles` must stay gated on `overrideColor` (gate added #2377, removed #4055, re-broke → #4419); safe now that #4392 light-bg rewriters handle #4028 zebra legibility; SAME rule paints vertical-TOC `.space`/▉ spacer cells (▉ U+2589 = blank glyph, contours=0) → "spacing changes" symptom; both fixed by the gate
|
||||
|
||||
## Library Fixes
|
||||
- [Tauri menu append race (#4389)](tauri-menu-append-race-4389.md) — un-awaited `Menu.append()` (async IPC) in `BookshelfItem.tsx` → context-menu items shuffle order every open (native only, invisible in jsdom); fix = single `await Menu.new({ items })` of ordered `MenuItemOptions`; order/inclusion extracted to pure `getBookContextMenuItemIds` for unit testing
|
||||
- [TXT author recognition (#4390)](txt-author-recognition-4390.md) — 【】-titled Chinese web-novels show author missing/garbage; they're TXT→EPUB (title==full filename is the tell, check `txt.ts` not foliate-js); `extractTxtFilenameMetadata` only handled 《》 + greedy header capture grabbed metadata blobs; fix = `parseLabeledAuthor` for any filename + `isPlausibleAuthorName` guard
|
||||
|
||||
## 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
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
---
|
||||
name: android-image-callout-freeze
|
||||
description: "Android WebView native long-press image callout collides with app touch handlers and freezes the app; reusable `.no-context-menu` fix"
|
||||
metadata:
|
||||
node_type: memory
|
||||
type: project
|
||||
originSessionId: 50bec34f-7090-4bf4-a194-9bf4029527bf
|
||||
---
|
||||
|
||||
Recurring Android-only freeze: long-pressing an `<img>` triggers the WebView's
|
||||
native image callout (context menu / drag / magnifier) which collides with the
|
||||
app's own touch handlers (long-press multi-select, or pinch/pan) and freezes the
|
||||
whole app until restart.
|
||||
|
||||
**Root cause:** `-webkit-touch-callout: none` does NOT inherit, so a
|
||||
`.no-context-menu` class on a *container* never reaches descendant images.
|
||||
|
||||
**Fix:** the `.no-context-menu img, .no-context-menu a` rule in
|
||||
`src/styles/globals.css` (sets `-webkit-touch-callout: none; -webkit-user-drag:
|
||||
none; user-select: none`). Apply the `no-context-menu` class to an *ancestor* of
|
||||
the image so the descendant rule reaches it. Harmless on desktop
|
||||
(`-webkit-touch-callout` is a no-op there; right-click-save still works).
|
||||
|
||||
Occurrences so far:
|
||||
- Book covers on the bookshelf — PR #4345 (`BookshelfItem.tsx`, gated on
|
||||
`appService?.isMobileApp`; added the `.no-context-menu img` rule).
|
||||
- Image preview / zoom viewer — issue #4420, `ImageViewer.tsx` root container
|
||||
(applied unconditionally — no selectable text there, so no need to gate).
|
||||
|
||||
**How to apply:** when a new "Android freezes on long-press of an image" report
|
||||
comes in, find the `<img>` and put `no-context-menu` on a containing element.
|
||||
@@ -0,0 +1,48 @@
|
||||
---
|
||||
name: booknote-view-autoscroll-4352
|
||||
description: "Annotation/bookmark list (BooknoteView) auto-scroll-to-nearest regression after virtualization (#4352) and its TOCView-mirroring fix"
|
||||
metadata:
|
||||
node_type: memory
|
||||
type: project
|
||||
originSessionId: bda988b9-28ec-450f-874e-ee9c104f7603
|
||||
---
|
||||
|
||||
After #4352 virtualized `BooknoteView` (sidebar annotations/bookmarks list,
|
||||
`src/app/reader/components/sidebar/BooknoteView.tsx`), the list stopped
|
||||
auto-scrolling to the nearest annotation for the current reading position (it
|
||||
stranded at the top showing Chapter 1). #4352 replaced the per-item
|
||||
`useScrollToItem` with a single `virtuosoRef.scrollToIndex`, but missed the
|
||||
machinery `TOCView` already had. Two distinct failure paths:
|
||||
|
||||
1. **Reload (annotations tab active at load; progress arrives AFTER mount):** the
|
||||
OverlayScrollbars `initialized` callback (deferred init) resets the wrapped
|
||||
viewport `scrollTop` to 0, clobbering the scroll; the `lastScrolledCfiRef`
|
||||
guard then blocks any retry. Fix = re-apply `scrollToIndex` to the *current*
|
||||
nearest index inside the `initialized` callback, read via a **ref**
|
||||
(`nearestIndexRef`) because that callback is the mount-time closure. Double
|
||||
rAF (settle the reset, then re-assert once rows are measured).
|
||||
2. **Tab-switch (open panel while reading; progress KNOWN at mount):** firing a
|
||||
`scrollToIndex` synchronously on the freshly mounted, unmeasured list either
|
||||
no-ops (`behavior:'smooth'`) or **wedges Virtuoso into rendering nothing**
|
||||
(`behavior:'auto'`). Fix = mount Virtuoso *natively* centered via
|
||||
`initialTopMostItemIndex` + an `initialScrollHandledRef` gate so the scroll
|
||||
effect SKIPS that first jump. This is exactly TOCView's design.
|
||||
|
||||
The fix mirrors [[toc-expand-and-autoscroll]] (same OverlayScrollbars-resets-
|
||||
scrollTop + Virtuoso-lands-short-on-unmeasured-rows pattern). Test:
|
||||
`src/__tests__/components/BooknoteView.test.tsx` stubs Virtuoso (spy-able
|
||||
`scrollToIndex`, captures `initialTopMostItemIndex`) and captures the mount-time
|
||||
`initialized` callback — forcing a ref-based fix (modeled on `TOCView.test.tsx`).
|
||||
|
||||
**Dev-server verification gotchas (cost hours here):**
|
||||
- The `localhost:3000` dev server was running from a *different worktree*
|
||||
(`/Users/chrox/dev/readest-fix-4394-bg-gutter-bleed`), not the main checkout.
|
||||
Edits weren't compiled until copied into that worktree's path. Check
|
||||
`ps aux | grep next-server` for the serving cwd. Book data is per-origin
|
||||
(OPFS/IndexedDB on localhost:3000) so you can't verify on another port.
|
||||
- After ~10 rapid file syncs, **Fast Refresh corrupts the mounted tab's state**
|
||||
— identical code that worked started rendering 0 items. A *brand-new tab*
|
||||
(close the old one) renders correctly. Always verify in a fresh tab.
|
||||
- Chrome MCP `javascript_tool` querying `.booknote-item` count catches Virtuoso
|
||||
mid-render (returns 0 even when it later paints fine). Trust the *screenshot*
|
||||
(the painted frame), not a synchronous DOM count, for virtualized lists.
|
||||
@@ -7,6 +7,9 @@ metadata:
|
||||
originSessionId: 1bd8a73e-f279-4ed8-9562-98e96d9723d5
|
||||
---
|
||||
|
||||
RESOLVED — merged. foliate-js `142bf11` (on main) + readest pointer bump/test
|
||||
(`fix(reader): show background texture in paginated mode (#4399)`).
|
||||
|
||||
#4399: a background texture (Settings → Color → texture, e.g. Leaves) shows in
|
||||
**scrolled** mode but is **absent in paginated** mode. The texture is NOT in the
|
||||
iframe — it's mounted on the HOST as `.foliate-viewer::before` (`src/styles/textures.ts`,
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
---
|
||||
name: paginator-gutter-bleed-asymmetry-4394
|
||||
description: "Paginated page background overflowed its column into the outer --_outer-min gutter (asymmetric on mixed spreads); fix = clamp computeBackgroundSegments to the content area, keep the grid"
|
||||
metadata:
|
||||
node_type: memory
|
||||
type: project
|
||||
originSessionId: 108a70bd-5af9-4a31-9b93-9d6df7637579
|
||||
---
|
||||
|
||||
#4394 "page viewport offset / content clipping." On a 2-up cover spread the
|
||||
left page (cover `<img>`, transparent page bg) had a white outer gutter while
|
||||
the right page (`<body class="c3">` body-colour yellow) bled its colour past its
|
||||
column into the right outer gutter → spread looked shifted right. On a wide
|
||||
screen (1920px, 720 max-inline) the gutters are ~250px so it reads as a "massive
|
||||
white blank gap." 0.10.6 filled colour edge-to-edge; 0.11.2 regressed.
|
||||
|
||||
**Geometry.** Grid `#top` = `minmax(--_outer-min-left,1fr) margin-left
|
||||
minmax(0,maxw) margin-right minmax(--_outer-min-right,1fr)` (foliate
|
||||
`paginator.js`). `#container` = grid-column 2/5 (inset by the outer gutter
|
||||
tracks); `#background` = grid-column 1/-1 (spans the gutters). `--_outer-min =
|
||||
(col_count-1)*(margin/4 + gap/4)` — non-zero ONLY in multi-column (0 in
|
||||
1-column), added in commit `0a0ceda` so the outer margin matches half the
|
||||
inter-column gap (symmetric spacing). `inset = containerLeft - bgLeft =
|
||||
--_outer-min-left`.
|
||||
|
||||
**Cause.** `computeBackgroundSegments` (the swipe-flash fix, commit `167757a`)
|
||||
positioned each page's colour segment at `inset + offset - scrollPos` and then
|
||||
STRETCHED any segment touching a container edge OUT to the bg edge (`start=0` /
|
||||
`end=bgSize`) — i.e. bled into the outer gutter. A body-coloured page bled into
|
||||
its gutter; a transparent/image page (cover) did not (its `#background` is
|
||||
transparent; its `<img>` is clamped inside the inset `#container`). Mixed spread
|
||||
→ one gutter coloured, one not → asymmetric.
|
||||
|
||||
**Fix (what the maintainer wanted).** Do NOT touch `--_outer-min` (it keeps the
|
||||
left/right margins symmetric with the centre gap). Instead CLAMP each segment to
|
||||
the content area so the background stays inside its column and never overflows
|
||||
into the outer gutter:
|
||||
```js
|
||||
const start = Math.max(segStart, containerStart) // was: if(...) start = 0
|
||||
const end = Math.min(segEnd, containerEnd) // was: if(...) end = bgSize
|
||||
if (end <= start) continue
|
||||
```
|
||||
`containerStart=inset`, `containerEnd=inset+containerSize`. In single-column the
|
||||
gutters are 0 (`--_outer-min`=0 → inset 0) so this still fills the viewport edge
|
||||
to edge (matches 0.10.6); in multi-column each page stays in its column with
|
||||
symmetric gutter margins. Image pages (cover/彩页) were already correct (`<img>`
|
||||
sits in its column); only the body-colour `#background` overflow needed fixing.
|
||||
|
||||
**Dead ends (took 2 wrong tries before the maintainer steered me right).**
|
||||
1. Thought it was the gutter-bleed and "gated" the stretch on both-edges-coloured
|
||||
— maintainer: "nothing to do with the gutter" (meaning don't change the bleed
|
||||
GATING, the real issue is the offset).
|
||||
2. Thought the title page shouldn't be yellow at all — it genuinely is
|
||||
`.c3{background:#ffe43f}` on `<body>`, captured as `docBackground` and painted
|
||||
full-bleed (body is zeroed via `doc.body.style.background='none'`, the old
|
||||
f087826 "dismiss iframe background, paint via root" design). Yellow is correct,
|
||||
it was just overflowing.
|
||||
3. Proposed dropping `--_outer-min` (revert `0a0ceda`) — maintainer rejected:
|
||||
that desymmetrises the centre-vs-side gaps. Keep the grid; fix the background.
|
||||
|
||||
**Verifying live.** foliate-js is a submodule; `next dev` / turbopack did NOT
|
||||
hot-reload an edit to `packages/foliate-js/paginator.js` even across full
|
||||
navigations — had to RESTART the dev server (`pnpm dev-web`, port 3000) to pick
|
||||
it up. Cheap interim check: clamp the live `#background` segment divs in the page
|
||||
and screenshot. Segment colours are render-timing dependent (a probe right after
|
||||
nav can show all-transparent before the body-colour is captured).
|
||||
|
||||
Tests: `apps/readest-app/src/__tests__/document/paginator-background-segments.test.ts`
|
||||
(tests "centered page" + "two-up spread" flipped from full-bleed to confined; the
|
||||
inset=0 swipe-flash tests are unchanged). Related: [[paginator-swipe-bg-flash]]
|
||||
[[paginated-texture-occlusion-4399]].
|
||||
@@ -0,0 +1,18 @@
|
||||
---
|
||||
name: progressbar-focus-ring-4397
|
||||
description: ProgressBar footer painted a stray focus-ring line on Android long-press; fix = remove tabIndex from the decorative div
|
||||
metadata:
|
||||
node_type: memory
|
||||
type: project
|
||||
originSessionId: 7d650c44-2d60-4a88-a9f5-6b5956bca59b
|
||||
---
|
||||
|
||||
#4397 "Strange line on the bottom of the page when long-pressing the footer" (Android 13, 0.11.2).
|
||||
|
||||
**Symptom:** a thin horizontal line, content-column wide, across the last text line / bottom margin. Appears after long-pressing the footer + turning a page; persists across page turns; cleared by double-tapping the footer + turning a page. Maintainer couldn't repro on eink (device/WebView-specific long-press focus behavior).
|
||||
|
||||
**Root cause:** `ProgressBar.tsx` (the always-on page-info footer, `.progressinfo`) rendered as `<div role='presentation' tabIndex={-1} onClick=...>`. The `tabIndex={-1}` made the decorative div click/touch-focusable. Android WebView long-press focused it and painted the browser default focus ring (`outline: auto`, matched `:focus-visible`). It's a top-document element pinned `absolute bottom-0` at book-view width, so the ring shows as a content-width box at the bottom on *every* page until focus clears. NOT a range/slider input — those are `opacity-0`/`visibility:hidden` (red herring: globals.css excludes `input[type='range']` from its outline-suppression rule, but that's unrelated here).
|
||||
|
||||
**Fix:** remove `tabIndex={-1}`. A `role='presentation'` decorative element must not be focusable. `onClick` (tap-to-cycle progress mode / dismiss annotation popup) fires regardless of tabindex, and no ancestor has a real `tabindex` to grab focus instead (verified: focus falls through to `<body>`). Nothing programmatically focuses `.progressinfo`. Test asserts `.progressinfo` has no `tabindex` attribute (in `ProgressBar.test.tsx`).
|
||||
|
||||
**Debugging technique that worked:** download the issue video (`gh` attachment URL) → `ffmpeg` extract frames → zoom on the line; the **downward corner at the right end** revealed it was a rectangular *outline* (focus box), not a strikethrough/selection. Then in the live dev app (`mcp__claude-in-chrome__javascript_tool`): `el.focus(); el.matches(':focus-visible'); getComputedStyle(el).outlineStyle` → returned `true` / `auto`, confirming the element + mechanism. See [[annotator-reader-fixes]] [[layout-ui-fixes]].
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
name: table-dark-mode-tint-4419
|
||||
description: "Dark-mode table tint must stay gated on overrideColor; blanket `table *` color-mix breaks plain tables AND vertical-TOC spacer cells"
|
||||
metadata:
|
||||
node_type: memory
|
||||
type: project
|
||||
originSessionId: 114eeb22-c9b6-480d-8305-3a3190855638
|
||||
---
|
||||
|
||||
`getColorStyles` in `src/utils/style.ts` has a rule:
|
||||
`blockquote, table * { background-color: color-mix(in srgb, ${bg} 80%, #000) }` in dark mode.
|
||||
The `table *` part **must** be gated on `overrideColor` — `${isDarkMode && overrideColor ? ...}`.
|
||||
|
||||
**This gate has regressed twice.** #2377 (PR #2379) first added it ("avoid overriding
|
||||
table background by default"). #4055 (commit `cead0f42e`, closes #4028/#4029) **removed**
|
||||
the gate to darken illegible white zebra-stripe rows — which re-broke it and caused #4419:
|
||||
*every* dark-mode table (and its cells) gets a tint a few shades off the page bg, even
|
||||
tables with no background of their own.
|
||||
|
||||
Why the gate is safe now: #4392 (`176b950c9`) added light-background rewriters that map
|
||||
only actually-light backgrounds → page `bg` in dark mode, regardless of overrideColor —
|
||||
`getDarkModeLightBackgroundOverrides` (inline styles) + the `transformStylesheet` dark-mode
|
||||
block (stylesheet rules, `isLightCssColor` luminance > 0.85). So #4028's white zebra rows
|
||||
stay legible without the blanket tint. The blockquote-only tint (standalone `blockquote {}`
|
||||
rule, from #2538) stays unconditional in dark mode — that's intended.
|
||||
|
||||
**Symptom #2 shares this root cause.** #4419 also reported "spacing between words changes"
|
||||
on a vertical (writing-mode) 目录/TOC page. Those CJK web-novel books lay the TOC out as a
|
||||
`<table>` with empty `<td>` spacer columns and `<span class="space">▉</span>` spacers
|
||||
(custom "space" font; ▉ U+2589 is a **blank glyph, contours=0** — pure advance width, no
|
||||
outline). The blanket `table *` rule paints a background on those spacer spans/cells,
|
||||
making the invisible gaps show as tinted blocks → looks like the spacing changed. It's the
|
||||
painted **background**, not text color, that reveals them — recoloring `.co0` (`color:#000`
|
||||
→ fg) does nothing because the glyph has no outline. Gating the tint fixes both symptoms.
|
||||
|
||||
Tests live in `src/__tests__/utils/style-get-styles.test.ts` (assert the `blockquote,
|
||||
table *` block has no `color-mix` when overrideColor false; keep the override-true and
|
||||
blockquote-still-tinted cases). Related: [[css-style-fixes]].
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
name: tauri-menu-append-race-4389
|
||||
description: "Un-awaited Tauri Menu.append() races on IPC → context menu items shuffle order randomly; fix = single Menu.new({ items })"
|
||||
metadata:
|
||||
node_type: memory
|
||||
type: project
|
||||
originSessionId: 8169b903-f66e-4c35-b90f-6b9110837588
|
||||
---
|
||||
|
||||
#4389 — the bookshelf right-click context menu (Windows/Edge WebView2) showed the
|
||||
same items but in a **randomly changing order** on every open.
|
||||
|
||||
**Root cause:** `Menu.append()` from `@tauri-apps/api/menu` returns `Promise<void>`
|
||||
— each call is an async IPC round-trip to the Rust backend. `BookshelfItem.tsx`
|
||||
fired ~11 `menu.append(item)` calls **without awaiting** (then `menu.popup()` also
|
||||
un-awaited). The concurrent IPC requests resolve in non-deterministic order on the
|
||||
Rust side, so items land shuffled. Synchronous JS call order is correct — the race
|
||||
is purely in async resolution, which is why it's invisible in jsdom and only shows
|
||||
on the native app.
|
||||
|
||||
**Fix:** build the items array in order and create the menu in ONE call:
|
||||
`const menu = await Menu.new({ items })` then `await menu.popup()`.
|
||||
`MenuOptions.items` / `append()` accept plain `MenuItemOptions` (`{ text, action }`)
|
||||
arrays — no need to pre-create `MenuItem.new()` per item at all. Applied to both
|
||||
book and group handlers.
|
||||
|
||||
**Testability:** extracted the order/inclusion logic into a pure
|
||||
`getBookContextMenuItemIds(book): BookContextMenuItemId[]` in
|
||||
`src/app/library/utils/libraryUtils.ts` (dep-light, already test-covered) so the
|
||||
deterministic ordering is unit-testable without mounting the component or mocking
|
||||
Tauri. Test: `src/__tests__/app/library/book-context-menu.test.ts`. The pure fn
|
||||
guards the order contract; the `Menu.new({ items })` wiring is what kills the race.
|
||||
|
||||
**General lesson:** any un-awaited sequence of Tauri/IPC mutations that must stay
|
||||
ordered (append, insert, etc.) is a latent race — batch into one call or await each.
|
||||
See [[bug-patterns]].
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
name: txt-author-recognition-4390
|
||||
metadata:
|
||||
node_type: memory
|
||||
type: project
|
||||
originSessionId: f151827f-0bf2-4307-ac92-e6077df54d19
|
||||
---
|
||||
|
||||
Issue #4390: imported Chinese web-novels showed author either **missing (未知)** or
|
||||
as an **irrelevant metadata blob** (e.g. `2024/08/01发表于:是否首发:是字数1023150字116:01`).
|
||||
|
||||
**Key debugging tell:** the displayed *title* was the **entire filename** including
|
||||
`作者:X` (e.g. `【月如无恨月长圆】(1-154)作者:陈西`). A real EPUB's `dc:title` would
|
||||
be just the book name. Full-filename title ⇒ the file went through Readest's
|
||||
**TXT→EPUB converter** (`bookService.ts` `/\.txt$/` → `TxtToEpubConverter`), which
|
||||
sets `bookTitle = base filename` in the no-`《》` branch of `extractTxtFilenameMetadata`.
|
||||
So "EPUB format" books can still be TXT-origin — check `src/utils/txt.ts`, not foliate-js,
|
||||
when title looks like a filename. (Native Rust EPUB parser #4369 was NOT shipped in 0.11.2.)
|
||||
|
||||
Two root causes in `src/utils/txt.ts`:
|
||||
1. `extractTxtFilenameMetadata` only pulled an author from `《》`-wrapped names; `【】`
|
||||
names (the web-novel norm) got no filename author.
|
||||
2. Greedy header capture `/[【\[]?作者[】\]]?[::\s]\s*(.+)\r?\n/` grabbed a whole noisy
|
||||
metadata line as the author.
|
||||
|
||||
Fix:
|
||||
- `parseLabeledAuthor(base)` extracts the labeled `作者:X` from ANY filename; title stays
|
||||
the full name. Only the *labeled* form is safe on a full filename — a bracketed/bare
|
||||
fallback would mistake a leading `【title】` for the author.
|
||||
- `isPlausibleAuthorName()` rejects a header match that looks like a blob (contains `:`/`:`,
|
||||
a `\d{4,}` run, or length > 20) → falls back to the filename author. Applied in BOTH
|
||||
`convertSmallFile` and `extractAuthorAndLanguage` (large-file path).
|
||||
|
||||
Note: the `著` form is handled for file *content* headers but NOT for filenames (kept minimal).
|
||||
Reported files were 455 kB / 1.25 MB → both under the 8 MB `LARGE_TXT_THRESHOLD_BYTES`
|
||||
(`convertSmallFile` path). Tests live in `txt-converter.test.ts` (faithful repro of both
|
||||
reported strings). Related: [[bug-patterns]].
|
||||
@@ -1827,5 +1827,9 @@
|
||||
"No Annotations": "لا توجد تعليقات توضيحية",
|
||||
"No Bookmarks": "لا توجد إشارات مرجعية",
|
||||
"Select some text to highlight": "حدّد نصًا لتمييزه",
|
||||
"Bookmark This Page": "أضف إشارة مرجعية لهذه الصفحة"
|
||||
"Bookmark This Page": "أضف إشارة مرجعية لهذه الصفحة",
|
||||
"Book file is not available locally": "ملف الكتاب غير متوفر محليًا",
|
||||
"Failed to send book": "فشل إرسال الكتاب",
|
||||
"Send": "إرسال",
|
||||
"Highlight Current Sentence": "تمييز الجملة الحالية"
|
||||
}
|
||||
|
||||
@@ -1695,5 +1695,9 @@
|
||||
"No Annotations": "কোনো টীকা নেই",
|
||||
"No Bookmarks": "কোনো বুকমার্ক নেই",
|
||||
"Select some text to highlight": "হাইলাইট করতে কিছু লেখা নির্বাচন করুন",
|
||||
"Bookmark This Page": "এই পৃষ্ঠা বুকমার্ক করুন"
|
||||
"Bookmark This Page": "এই পৃষ্ঠা বুকমার্ক করুন",
|
||||
"Book file is not available locally": "বইয়ের ফাইলটি স্থানীয়ভাবে উপলব্ধ নয়",
|
||||
"Failed to send book": "বই পাঠাতে ব্যর্থ হয়েছে",
|
||||
"Send": "পাঠান",
|
||||
"Highlight Current Sentence": "বর্তমান বাক্য হাইলাইট করুন"
|
||||
}
|
||||
|
||||
@@ -1662,5 +1662,9 @@
|
||||
"No Annotations": "མཆན་མེད།",
|
||||
"No Bookmarks": "དཔེ་རྟགས་མེད།",
|
||||
"Select some text to highlight": "འོག་ཐིག་གཏོང་བར་ཡི་གེ་འདེམས་རོགས།",
|
||||
"Bookmark This Page": "ཤོག་ངོས་འདིར་དཔེ་རྟགས་འགོད།"
|
||||
"Bookmark This Page": "ཤོག་ངོས་འདིར་དཔེ་རྟགས་འགོད།",
|
||||
"Book file is not available locally": "དེབ་ཀྱི་ཡིག་ཆ་དེ་ས་གནས་སུ་མི་འདུག",
|
||||
"Failed to send book": "དེབ་སྐུར་གཏོང་མ་ཐུབ།",
|
||||
"Send": "སྐུར་གཏོང་།",
|
||||
"Highlight Current Sentence": "ད་ལྟའི་ཚིག་གྲུབ་ལ་འོག་ཐིག་གཏོང་།"
|
||||
}
|
||||
|
||||
@@ -1695,5 +1695,9 @@
|
||||
"No Annotations": "Keine Anmerkungen",
|
||||
"No Bookmarks": "Keine Lesezeichen",
|
||||
"Select some text to highlight": "Wähle Text zum Hervorheben aus",
|
||||
"Bookmark This Page": "Diese Seite mit Lesezeichen versehen"
|
||||
"Bookmark This Page": "Diese Seite mit Lesezeichen versehen",
|
||||
"Book file is not available locally": "Buchdatei ist nicht lokal verfügbar",
|
||||
"Failed to send book": "Senden des Buchs fehlgeschlagen",
|
||||
"Send": "Senden",
|
||||
"Highlight Current Sentence": "Aktuellen Satz hervorheben"
|
||||
}
|
||||
|
||||
@@ -1695,5 +1695,9 @@
|
||||
"No Annotations": "Κανένας σχολιασμός",
|
||||
"No Bookmarks": "Κανένας σελιδοδείκτης",
|
||||
"Select some text to highlight": "Επιλέξτε κείμενο για επισήμανση",
|
||||
"Bookmark This Page": "Προσθήκη σελιδοδείκτη σε αυτή τη σελίδα"
|
||||
"Bookmark This Page": "Προσθήκη σελιδοδείκτη σε αυτή τη σελίδα",
|
||||
"Book file is not available locally": "Το αρχείο του βιβλίου δεν είναι διαθέσιμο τοπικά",
|
||||
"Failed to send book": "Αποτυχία αποστολής βιβλίου",
|
||||
"Send": "Αποστολή",
|
||||
"Highlight Current Sentence": "Επισήμανση τρέχουσας πρότασης"
|
||||
}
|
||||
|
||||
@@ -1728,5 +1728,9 @@
|
||||
"No Annotations": "Sin anotaciones",
|
||||
"No Bookmarks": "Sin marcadores",
|
||||
"Select some text to highlight": "Selecciona texto para resaltar",
|
||||
"Bookmark This Page": "Añadir esta página a marcadores"
|
||||
"Bookmark This Page": "Añadir esta página a marcadores",
|
||||
"Book file is not available locally": "El archivo del libro no está disponible localmente",
|
||||
"Failed to send book": "No se pudo enviar el libro",
|
||||
"Send": "Enviar",
|
||||
"Highlight Current Sentence": "Resaltar la oración actual"
|
||||
}
|
||||
|
||||
@@ -1695,5 +1695,9 @@
|
||||
"No Annotations": "هیچ حاشیهنویسیای نیست",
|
||||
"No Bookmarks": "هیچ نشانکی نیست",
|
||||
"Select some text to highlight": "برای هایلایت کردن، متنی را انتخاب کنید",
|
||||
"Bookmark This Page": "افزودن این صفحه به نشانکها"
|
||||
"Bookmark This Page": "افزودن این صفحه به نشانکها",
|
||||
"Book file is not available locally": "فایل کتاب به صورت محلی در دسترس نیست",
|
||||
"Failed to send book": "ارسال کتاب ناموفق بود",
|
||||
"Send": "ارسال",
|
||||
"Highlight Current Sentence": "برجستهسازی جملهٔ فعلی"
|
||||
}
|
||||
|
||||
@@ -1728,5 +1728,9 @@
|
||||
"No Annotations": "Aucune annotation",
|
||||
"No Bookmarks": "Aucun signet",
|
||||
"Select some text to highlight": "Sélectionnez du texte à surligner",
|
||||
"Bookmark This Page": "Ajouter cette page aux signets"
|
||||
"Bookmark This Page": "Ajouter cette page aux signets",
|
||||
"Book file is not available locally": "Le fichier du livre n'est pas disponible localement",
|
||||
"Failed to send book": "Échec de l'envoi du livre",
|
||||
"Send": "Envoyer",
|
||||
"Highlight Current Sentence": "Surligner la phrase actuelle"
|
||||
}
|
||||
|
||||
@@ -1728,5 +1728,9 @@
|
||||
"No Annotations": "אין הדגשות",
|
||||
"No Bookmarks": "אין סימניות",
|
||||
"Select some text to highlight": "בחרו טקסט להדגשה",
|
||||
"Bookmark This Page": "הוסיפו דף זה לסימניות"
|
||||
"Bookmark This Page": "הוסיפו דף זה לסימניות",
|
||||
"Book file is not available locally": "קובץ הספר אינו זמין באופן מקומי",
|
||||
"Failed to send book": "שליחת הספר נכשלה",
|
||||
"Send": "שליחה",
|
||||
"Highlight Current Sentence": "הדגשת המשפט הנוכחי"
|
||||
}
|
||||
|
||||
@@ -1695,5 +1695,9 @@
|
||||
"No Annotations": "कोई टिप्पणी नहीं",
|
||||
"No Bookmarks": "कोई बुकमार्क नहीं",
|
||||
"Select some text to highlight": "हाइलाइट करने के लिए कुछ टेक्स्ट चुनें",
|
||||
"Bookmark This Page": "इस पृष्ठ को बुकमार्क करें"
|
||||
"Bookmark This Page": "इस पृष्ठ को बुकमार्क करें",
|
||||
"Book file is not available locally": "पुस्तक फ़ाइल स्थानीय रूप से उपलब्ध नहीं है",
|
||||
"Failed to send book": "पुस्तक भेजने में विफल",
|
||||
"Send": "भेजें",
|
||||
"Highlight Current Sentence": "वर्तमान वाक्य को हाइलाइट करें"
|
||||
}
|
||||
|
||||
@@ -1695,5 +1695,9 @@
|
||||
"No Annotations": "Nincsenek megjegyzések",
|
||||
"No Bookmarks": "Nincsenek könyvjelzők",
|
||||
"Select some text to highlight": "Jelölj ki szöveget a kiemeléshez",
|
||||
"Bookmark This Page": "Oldal hozzáadása a könyvjelzőkhöz"
|
||||
"Bookmark This Page": "Oldal hozzáadása a könyvjelzőkhöz",
|
||||
"Book file is not available locally": "A könyvfájl nem érhető el helyileg",
|
||||
"Failed to send book": "A könyv küldése sikertelen",
|
||||
"Send": "Küldés",
|
||||
"Highlight Current Sentence": "Aktuális mondat kiemelése"
|
||||
}
|
||||
|
||||
@@ -1662,5 +1662,9 @@
|
||||
"No Annotations": "Tidak ada anotasi",
|
||||
"No Bookmarks": "Tidak ada penanda",
|
||||
"Select some text to highlight": "Pilih teks untuk disorot",
|
||||
"Bookmark This Page": "Tandai halaman ini"
|
||||
"Bookmark This Page": "Tandai halaman ini",
|
||||
"Book file is not available locally": "Berkas buku tidak tersedia secara lokal",
|
||||
"Failed to send book": "Gagal mengirim buku",
|
||||
"Send": "Kirim",
|
||||
"Highlight Current Sentence": "Sorot kalimat saat ini"
|
||||
}
|
||||
|
||||
@@ -1728,5 +1728,9 @@
|
||||
"No Annotations": "Nessuna annotazione",
|
||||
"No Bookmarks": "Nessun segnalibro",
|
||||
"Select some text to highlight": "Seleziona del testo da evidenziare",
|
||||
"Bookmark This Page": "Aggiungi questa pagina ai segnalibri"
|
||||
"Bookmark This Page": "Aggiungi questa pagina ai segnalibri",
|
||||
"Book file is not available locally": "Il file del libro non è disponibile localmente",
|
||||
"Failed to send book": "Invio del libro non riuscito",
|
||||
"Send": "Invia",
|
||||
"Highlight Current Sentence": "Evidenzia la frase corrente"
|
||||
}
|
||||
|
||||
@@ -1662,5 +1662,9 @@
|
||||
"No Annotations": "注釈がありません",
|
||||
"No Bookmarks": "ブックマークがありません",
|
||||
"Select some text to highlight": "ハイライトするテキストを選択してください",
|
||||
"Bookmark This Page": "このページをブックマーク"
|
||||
"Bookmark This Page": "このページをブックマーク",
|
||||
"Book file is not available locally": "書籍ファイルがローカルに存在しません",
|
||||
"Failed to send book": "書籍の送信に失敗しました",
|
||||
"Send": "送信",
|
||||
"Highlight Current Sentence": "現在の文をハイライト"
|
||||
}
|
||||
|
||||
@@ -1662,5 +1662,9 @@
|
||||
"No Annotations": "주석 없음",
|
||||
"No Bookmarks": "북마크 없음",
|
||||
"Select some text to highlight": "하이라이트할 텍스트를 선택하세요",
|
||||
"Bookmark This Page": "이 페이지 북마크"
|
||||
"Bookmark This Page": "이 페이지 북마크",
|
||||
"Book file is not available locally": "책 파일을 로컬에서 사용할 수 없습니다",
|
||||
"Failed to send book": "책 보내기에 실패했습니다",
|
||||
"Send": "보내기",
|
||||
"Highlight Current Sentence": "현재 문장 강조"
|
||||
}
|
||||
|
||||
@@ -1662,5 +1662,9 @@
|
||||
"No Annotations": "Tiada anotasi",
|
||||
"No Bookmarks": "Tiada penanda buku",
|
||||
"Select some text to highlight": "Pilih teks untuk ditonjolkan",
|
||||
"Bookmark This Page": "Tanda halaman ini"
|
||||
"Bookmark This Page": "Tanda halaman ini",
|
||||
"Book file is not available locally": "Fail buku tidak tersedia secara setempat",
|
||||
"Failed to send book": "Gagal menghantar buku",
|
||||
"Send": "Hantar",
|
||||
"Highlight Current Sentence": "Serlahkan ayat semasa"
|
||||
}
|
||||
|
||||
@@ -1695,5 +1695,9 @@
|
||||
"No Annotations": "Geen aantekeningen",
|
||||
"No Bookmarks": "Geen bladwijzers",
|
||||
"Select some text to highlight": "Selecteer tekst om te markeren",
|
||||
"Bookmark This Page": "Deze pagina als bladwijzer toevoegen"
|
||||
"Bookmark This Page": "Deze pagina als bladwijzer toevoegen",
|
||||
"Book file is not available locally": "Boekbestand is niet lokaal beschikbaar",
|
||||
"Failed to send book": "Verzenden van boek mislukt",
|
||||
"Send": "Verzenden",
|
||||
"Highlight Current Sentence": "Huidige zin markeren"
|
||||
}
|
||||
|
||||
@@ -1761,5 +1761,9 @@
|
||||
"No Annotations": "Brak adnotacji",
|
||||
"No Bookmarks": "Brak zakładek",
|
||||
"Select some text to highlight": "Wybierz tekst do podświetlenia",
|
||||
"Bookmark This Page": "Dodaj tę stronę do zakładek"
|
||||
"Bookmark This Page": "Dodaj tę stronę do zakładek",
|
||||
"Book file is not available locally": "Plik książki nie jest dostępny lokalnie",
|
||||
"Failed to send book": "Nie udało się wysłać książki",
|
||||
"Send": "Wyślij",
|
||||
"Highlight Current Sentence": "Podświetl bieżące zdanie"
|
||||
}
|
||||
|
||||
@@ -1728,5 +1728,9 @@
|
||||
"No Annotations": "Nenhuma anotação",
|
||||
"No Bookmarks": "Nenhum marcador",
|
||||
"Select some text to highlight": "Selecione um texto para destacar",
|
||||
"Bookmark This Page": "Adicionar esta página aos marcadores"
|
||||
"Bookmark This Page": "Adicionar esta página aos marcadores",
|
||||
"Book file is not available locally": "O arquivo do livro não está disponível localmente",
|
||||
"Failed to send book": "Falha ao enviar o livro",
|
||||
"Send": "Enviar",
|
||||
"Highlight Current Sentence": "Destacar a frase atual"
|
||||
}
|
||||
|
||||
@@ -1728,5 +1728,9 @@
|
||||
"No Annotations": "Sem anotações",
|
||||
"No Bookmarks": "Sem marcadores",
|
||||
"Select some text to highlight": "Selecione texto para destacar",
|
||||
"Bookmark This Page": "Adicionar esta página aos marcadores"
|
||||
"Bookmark This Page": "Adicionar esta página aos marcadores",
|
||||
"Book file is not available locally": "O ficheiro do livro não está disponível localmente",
|
||||
"Failed to send book": "Falha ao enviar o livro",
|
||||
"Send": "Enviar",
|
||||
"Highlight Current Sentence": "Destacar a frase atual"
|
||||
}
|
||||
|
||||
@@ -1728,5 +1728,9 @@
|
||||
"No Annotations": "Nicio adnotare",
|
||||
"No Bookmarks": "Niciun marcaj",
|
||||
"Select some text to highlight": "Selectează text pentru evidențiere",
|
||||
"Bookmark This Page": "Adaugă această pagină la marcaje"
|
||||
"Bookmark This Page": "Adaugă această pagină la marcaje",
|
||||
"Book file is not available locally": "Fișierul cărții nu este disponibil local",
|
||||
"Failed to send book": "Trimiterea cărții a eșuat",
|
||||
"Send": "Trimite",
|
||||
"Highlight Current Sentence": "Evidențiază propoziția curentă"
|
||||
}
|
||||
|
||||
@@ -1761,5 +1761,9 @@
|
||||
"No Annotations": "Нет аннотаций",
|
||||
"No Bookmarks": "Нет закладок",
|
||||
"Select some text to highlight": "Выберите текст для выделения",
|
||||
"Bookmark This Page": "Добавить эту страницу в закладки"
|
||||
"Bookmark This Page": "Добавить эту страницу в закладки",
|
||||
"Book file is not available locally": "Файл книги недоступен локально",
|
||||
"Failed to send book": "Не удалось отправить книгу",
|
||||
"Send": "Отправить",
|
||||
"Highlight Current Sentence": "Выделять текущее предложение"
|
||||
}
|
||||
|
||||
@@ -1695,5 +1695,9 @@
|
||||
"No Annotations": "අනුසටහන් නැත",
|
||||
"No Bookmarks": "පොත් සලකුණු නැත",
|
||||
"Select some text to highlight": "ඉස්මතු කිරීමට පෙළක් තෝරන්න",
|
||||
"Bookmark This Page": "මෙම පිටුව පොත් සලකුණක් කරන්න"
|
||||
"Bookmark This Page": "මෙම පිටුව පොත් සලකුණක් කරන්න",
|
||||
"Book file is not available locally": "පොත් ගොනුව දේශීයව ලබා ගත නොහැක",
|
||||
"Failed to send book": "පොත යැවීමට අසමත් විය",
|
||||
"Send": "යවන්න",
|
||||
"Highlight Current Sentence": "වර්තමාන වාක්යය ඉස්මතු කරන්න"
|
||||
}
|
||||
|
||||
@@ -1761,5 +1761,9 @@
|
||||
"No Annotations": "Ni opomb",
|
||||
"No Bookmarks": "Ni zaznamkov",
|
||||
"Select some text to highlight": "Izberite besedilo za označevanje",
|
||||
"Bookmark This Page": "Dodaj to stran med zaznamke"
|
||||
"Bookmark This Page": "Dodaj to stran med zaznamke",
|
||||
"Book file is not available locally": "Datoteka knjige ni na voljo lokalno",
|
||||
"Failed to send book": "Pošiljanje knjige ni uspelo",
|
||||
"Send": "Pošlji",
|
||||
"Highlight Current Sentence": "Označi trenutni stavek"
|
||||
}
|
||||
|
||||
@@ -1695,5 +1695,9 @@
|
||||
"No Annotations": "Inga kommentarer",
|
||||
"No Bookmarks": "Inga bokmärken",
|
||||
"Select some text to highlight": "Markera text att framhäva",
|
||||
"Bookmark This Page": "Bokmärk den här sidan"
|
||||
"Bookmark This Page": "Bokmärk den här sidan",
|
||||
"Book file is not available locally": "Bokfilen är inte tillgänglig lokalt",
|
||||
"Failed to send book": "Det gick inte att skicka boken",
|
||||
"Send": "Skicka",
|
||||
"Highlight Current Sentence": "Markera aktuell mening"
|
||||
}
|
||||
|
||||
@@ -1695,5 +1695,9 @@
|
||||
"No Annotations": "சிறுகுறிப்புகள் இல்லை",
|
||||
"No Bookmarks": "புக்மார்க்குகள் இல்லை",
|
||||
"Select some text to highlight": "சிறப்பிக்க உரையைத் தேர்ந்தெடுக்கவும்",
|
||||
"Bookmark This Page": "இந்தப் பக்கத்தைப் புக்மார்க் செய்யவும்"
|
||||
"Bookmark This Page": "இந்தப் பக்கத்தைப் புக்மார்க் செய்யவும்",
|
||||
"Book file is not available locally": "புத்தக கோப்பு உள்ளூரில் கிடைக்கவில்லை",
|
||||
"Failed to send book": "புத்தகத்தை அனுப்ப முடியவில்லை",
|
||||
"Send": "அனுப்பு",
|
||||
"Highlight Current Sentence": "தற்போதைய வாக்கியத்தை தனிப்படுத்து"
|
||||
}
|
||||
|
||||
@@ -1662,5 +1662,9 @@
|
||||
"No Annotations": "ไม่มีคำอธิบายประกอบ",
|
||||
"No Bookmarks": "ไม่มีบุ๊กมาร์ก",
|
||||
"Select some text to highlight": "เลือกข้อความเพื่อไฮไลต์",
|
||||
"Bookmark This Page": "บุ๊กมาร์กหน้านี้"
|
||||
"Bookmark This Page": "บุ๊กมาร์กหน้านี้",
|
||||
"Book file is not available locally": "ไม่มีไฟล์หนังสือในเครื่อง",
|
||||
"Failed to send book": "ส่งหนังสือไม่สำเร็จ",
|
||||
"Send": "ส่ง",
|
||||
"Highlight Current Sentence": "ไฮไลต์ประโยคปัจจุบัน"
|
||||
}
|
||||
|
||||
@@ -1695,5 +1695,9 @@
|
||||
"No Annotations": "Açıklama yok",
|
||||
"No Bookmarks": "Yer işareti yok",
|
||||
"Select some text to highlight": "Vurgulamak için metin seçin",
|
||||
"Bookmark This Page": "Bu Sayfayı Yer İşaretine Ekle"
|
||||
"Bookmark This Page": "Bu Sayfayı Yer İşaretine Ekle",
|
||||
"Book file is not available locally": "Kitap dosyası yerel olarak mevcut değil",
|
||||
"Failed to send book": "Kitap gönderilemedi",
|
||||
"Send": "Gönder",
|
||||
"Highlight Current Sentence": "Geçerli cümleyi vurgula"
|
||||
}
|
||||
|
||||
@@ -1761,5 +1761,9 @@
|
||||
"No Annotations": "Немає анотацій",
|
||||
"No Bookmarks": "Немає закладок",
|
||||
"Select some text to highlight": "Виділіть текст для підсвічування",
|
||||
"Bookmark This Page": "Додати цю сторінку до закладок"
|
||||
"Bookmark This Page": "Додати цю сторінку до закладок",
|
||||
"Book file is not available locally": "Файл книги недоступний локально",
|
||||
"Failed to send book": "Не вдалося надіслати книгу",
|
||||
"Send": "Надіслати",
|
||||
"Highlight Current Sentence": "Виділяти поточне речення"
|
||||
}
|
||||
|
||||
@@ -1695,5 +1695,9 @@
|
||||
"No Annotations": "Izohlar yoʻq",
|
||||
"No Bookmarks": "Xatchoʻplar yoʻq",
|
||||
"Select some text to highlight": "Belgilash uchun matn tanlang",
|
||||
"Bookmark This Page": "Bu sahifani xatchoʻpga qoʻshish"
|
||||
"Bookmark This Page": "Bu sahifani xatchoʻpga qoʻshish",
|
||||
"Book file is not available locally": "Kitob fayli mahalliy tarzda mavjud emas",
|
||||
"Failed to send book": "Kitobni yuborib bo'lmadi",
|
||||
"Send": "Yuborish",
|
||||
"Highlight Current Sentence": "Joriy jumlani ajratib ko'rsatish"
|
||||
}
|
||||
|
||||
@@ -1662,5 +1662,9 @@
|
||||
"No Annotations": "Không có chú thích",
|
||||
"No Bookmarks": "Không có dấu trang",
|
||||
"Select some text to highlight": "Chọn một đoạn văn bản để tô sáng",
|
||||
"Bookmark This Page": "Đánh dấu trang này"
|
||||
"Bookmark This Page": "Đánh dấu trang này",
|
||||
"Book file is not available locally": "Tệp sách không có sẵn trên thiết bị",
|
||||
"Failed to send book": "Gửi sách thất bại",
|
||||
"Send": "Gửi",
|
||||
"Highlight Current Sentence": "Tô sáng câu hiện tại"
|
||||
}
|
||||
|
||||
@@ -1662,5 +1662,9 @@
|
||||
"No Annotations": "暂无注释",
|
||||
"No Bookmarks": "暂无书签",
|
||||
"Select some text to highlight": "选择文本以划线",
|
||||
"Bookmark This Page": "为此页添加书签"
|
||||
"Bookmark This Page": "为此页添加书签",
|
||||
"Book file is not available locally": "本地没有该图书文件",
|
||||
"Failed to send book": "发送图书失败",
|
||||
"Send": "发送",
|
||||
"Highlight Current Sentence": "高亮当前句子"
|
||||
}
|
||||
|
||||
@@ -1662,5 +1662,9 @@
|
||||
"No Annotations": "尚無註解",
|
||||
"No Bookmarks": "尚無書籤",
|
||||
"Select some text to highlight": "選取文字以劃線",
|
||||
"Bookmark This Page": "將此頁加入書籤"
|
||||
"Bookmark This Page": "將此頁加入書籤",
|
||||
"Book file is not available locally": "本機沒有此書籍檔案",
|
||||
"Failed to send book": "傳送書籍失敗",
|
||||
"Send": "傳送",
|
||||
"Highlight Current Sentence": "醒目標示目前句子"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user