Skip to content

fix(web): translate series, calendar and push all strings, fix search interval hint - #3027

Merged
vavallee merged 5 commits into
mainfrom
fix/web-english-strings
Oct 6, 2026
Merged

vavallee merged 5 commits into
mainfrom
fix/web-english-strings

Conversation

@vavallee

@vavallee vavallee commented Oct 6, 2026 •

Copy link
Copy Markdown
Owner

Summary

Fixes UI text bugs found while translating Norwegian and Swedish in #2994.

  1. Wrong advice in the search interval hint. search.interval is the gap between scheduled sweeps (internal/scheduler/scheduler.go registers search-wanted as @every <interval>, clamped to 1h to 168h, read once at startup). The hint told rate limited users to reduce it, which makes Bindery search more often. It now says to choose a longer interval.
  2. Hardcoded English in the Series page, the Calendar and Calibre's Push all to Calibre now goes through i18n.
  3. Ellipses normalised to … across en.json prose, and in the other locales where it is mechanical.

Implementation notes

Area Change
searchIntervalHint Reworded in en.json. de, es, fr, nl, tl and id held the old wrong advice as untranslated English, so the key is removed there and falls back to the corrected English. ko never had the key.
CalendarPage The month label no longer wraps on phones (whitespace-nowrap, min width). Hardcoded month and weekday arrays replaced with Intl.DateTimeFormat in i18n.resolvedLanguage, built and formatted in UTC so no offset can shift a label. Covers the month header, the releasingIn interpolation, the agenda day labels (Mar 5 / 5 mars), both weekday header rows and the document title.
SeriesPage Heading, count (pluralised), Add Series, Rename, Add Book, Delete, empty states, missing and book count badges (pluralised), the shortlist switch, genre button, prompt, tooltips and results, Fill gaps and its results, the Hardcover panel (link button, heading, diff summary, format select, add / add all, loading, more missing), the status pill (now uses the existing bookStatus.* labels) and the document title. New keys are under series.*.
SeriesNameModal (sibling) Name label, placeholder, validation and error messages, Cancel, Saving. The Add Series button and modal title in AuthorsPage now use the same key.
CalibreTab The Push all to Calibre block (label, description, bridge warning, button, tooltip, queueing) and the whole CalibreSyncModal. New keys are under settings.calibre.pushAll.*.
Ellipses en.json: 23 values ... to …, plus the matching inline t() defaults so inlineDefaults.test.ts stays green. de, es, fr, nl, tl, id and ko: translated values only. Values that were still verbatim English filler (common.deleting in de, es, fr, nl, tl and id, and discover.refreshing in de, fr and nl) are removed so they fall back to en.json. nb.json and sv.json were not touched (#2994).

English rendering is unchanged except: ellipses, Genres set on N books (was book(s)), N more missing book singular, and series status pills now read Imported / Wanted (capitalised, as on other pages) instead of the raw enum.

Follow-ups (not in this PR)

  • The rest of CalibreTab.tsx is still hardcoded English: library path, write integration options, binary path, plugin URL, API key, path remap, Test connection, Library import, CWA section and import progress.
  • AddSeriesBookModal.tsx and HardcoverSeriesLinkModal.tsx (opened from the Series page) are hardcoded English.
  • The search interval <option> labels in GeneralTab.tsx (6 hours, 12 hours (default) and so on).
  • ABSTab.tsx has hardcoded Searching..., Author, Book.
  • Most pages set document.title from an English literal (Wanted, Search, History, Settings, Books, Users).
  • The non-English locales still carry other English filler without ellipses, for example searchIntervalLabel and searchIntervalRestart. These should be removed so they fall back properly.

Checklist

  • Tests added or updated
  • Doc-update gate cleared (no docs describe these strings)
  • Wiki pages updated if user-facing behaviour changed (not applicable)

Test plan

  • cd web && npm ci && npm run lint (0 errors; the 7 warnings are already on main)
  • npm run typecheck
  • npx vitest run: 1226 of 1227 passed. The one failure was a 5s timeout in HistoryPage.test.tsx (a file this PR does not touch), and it passes when run on its own.
  • npm run build
  • After merging main (test(web): cover the least covered settings tabs, pages and API modules #3032): CalibreTab.coverage.test.tsx now matches on i18n keys. The full suite passed 1547 of 1548; the one failure was a library import timing test in that file, in a section this PR does not change, and the file passed 3 of 3 runs on its own.
  • New tests: CalendarPage.test.tsx (English and French month, weekday and day labels), SeriesPage.test.tsx "renders its labels from the active locale", CalibreSyncModal.test.tsx translation cases. I ran the locale tests against the original components and all three failed, so they do catch the old hardcoded strings.

🤖 Generated with Claude Code

https://claude.ai/code/session_016fJcCVbNnKsmj2MAAMwWj9

… interval hint

The wanted search interval is the gap between scheduled sweeps
(scheduler.go runs search-wanted "@every <interval>"), so the hint telling
rate limited users to reduce it made Bindery search more often. It now
says to choose a longer interval. The de, es, fr, nl, tl and id files
carried the same wrong advice as untranslated English, so the key is
removed there and falls back to the corrected English.

The Series page, its name modal, the Calendar month and weekday names and
the Calibre Push all button and progress modal were hardcoded English.
They now go through i18n; the Calendar formats names with
Intl.DateTimeFormat in the active language.

en.json prose now uses the ellipsis character throughout, with matching
inline defaults. The other locales (excluding nb and sv, which #2994
handles) get the same change for translated values only; English filler
values are left alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016fJcCVbNnKsmj2MAAMwWj9
Signed-off-by: vavallee <vavallee@protonmail.com>
@codecov

codecov Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 69.23077% with 16 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
web/src/pages/SeriesPage.tsx 68.18% 4 Missing and 3 partials ⚠️
web/src/pages/settings/CalibreTab.tsx 45.45% 0 Missing and 6 partials ⚠️
web/src/components/SeriesNameModal.tsx 50.00% 2 Missing ⚠️
web/src/pages/CalendarPage.tsx 90.00% 0 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

vavallee and others added 3 commits October 6, 2026 15:00
… line

Removes common.deleting and discover.refreshing from locales where they
were still verbatim English, so they fall back to en.json like the
search interval hint. The calendar month label no longer wraps on phones
for long names such as Spanish "septiembre de 2026". The changelog
fragment carries the PR number and notes the capitalised series badges.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016fJcCVbNnKsmj2MAAMwWj9
Signed-off-by: vavallee <vavallee@protonmail.com>
Signed-off-by: vavallee <vavallee@protonmail.com>
The Push all block and its modal now go through t(), and this file mocks
t to return keys, so the helper and the assertions for the description,
the bridge warning, the missing plugin URL tooltip, the done summary and
the Close button match on keys instead of English text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016fJcCVbNnKsmj2MAAMwWj9
Signed-off-by: vavallee <vavallee@protonmail.com>
@vavallee
vavallee marked this pull request as ready for review October 6, 2026 18:17
@vavallee
vavallee enabled auto-merge (squash) October 6, 2026 18:17

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the full diff. Overall clean i18n sweep with good test coverage for locale-switching cases.

One item to verify before merge

SeriesPage.tsx (the Hardcover format select, around the fillMediaType block) now calls t('common.ebook'), t('common.audiobook'), and t('common.both') with no inline defaults, and the diff does not add these three keys to en.json. If they do not pre-exist in en.json the format select renders the raw key string (common.ebook etc.) in every language. The new SeriesPage.test.tsx locale test does not exercise the diff panel (it needs a hardcoverLink with missingCount > 0), so the gap would not be caught by CI. Worth a quick check: jq '.common | keys' web/src/i18n/locales/en.json

Everything else looks good

  • The search-interval hint fix is correct: the scheduler registers the job as @every interval (clamped 1h-168h), so a shorter interval searches more often. Old advice was backwards; new wording is right.
  • CalendarPage uses timeZone UTC throughout and anchors weekday names to 2023-01-01 (a Sunday, matching getDay()==0). UTC construction prevents local-offset edge cases. Switching key={d} to key={i} on weekday headers is correct since narrow names can collide (two S, two T).
  • Removing searchIntervalHint and other stale English filler from de/es/fr/nl/tl/id correctly falls back to the fixed en.json rather than showing old wrong advice.
  • Pluralisation of series.genre.applied (_one/_other) replaces the hacky book(s) suffix.
  • The three new test suites (CalendarPage, SeriesPage translations, CalibreSyncModal translations) each assert on the pre-fix behaviour failing, so they actually catch the regression they describe.

--- bot Bindery triage bot (automated). Reply to correct me; a human will see it.

@vavallee
vavallee merged commit 88dae5d into main Oct 6, 2026
40 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant