Skip to content

feat: add bilingual article translation system#119

Open
GoToBoy wants to merge 9 commits into
LeslieLeung:mainfrom
GoToBoy:feat/translation-only
Open

feat: add bilingual article translation system#119
GoToBoy wants to merge 9 commits into
LeslieLeung:mainfrom
GoToBoy:feat/translation-only

Conversation

@GoToBoy
Copy link
Copy Markdown
Contributor

@GoToBoy GoToBoy commented Feb 15, 2026

Summary

  • Add full-stack bilingual translation system with backend API, worker tasks, and frontend UI
  • Support multiple translation providers: Google Translate (free), DeepL, and OpenAI
  • Implement viewport-based sentence-level translation with paragraph caching
  • Add immersive bilingual display mode showing original and translated text side by side
  • User-configurable translation provider settings in the Settings page
  • Add database model for caching entry translations
  • Support i18n for all translation-related UI (en/zh-CN)

Changes

Backend

  • New EntryTranslation database model with Alembic migration
  • Translation API endpoints in entries router (translate entry, batch translate texts)
  • TranslationService with provider abstraction (Google/DeepL/OpenAI)
  • Worker task for background translation processing
  • Tests for translation service and API endpoints

Frontend

  • TranslationTab component in Settings for provider configuration
  • useViewportTranslation hook for viewport-based sentence-level translation
  • useEntryTranslation hook for full entry translation
  • Language detection and sentence splitting utilities
  • Bilingual display styles in ArticleReader

Test plan

  • Configure translation provider in Settings
  • Translate an article using the translate button in ArticleReader
  • Verify viewport-based translation highlights sentences on scroll
  • Verify paragraph translation caching works (re-translate same content)
  • Test with Google Translate (no API key needed)
  • Test fallback to Google when DeepL/OpenAI key is missing

🤖 Generated with Claude Code

GoToBoy and others added 9 commits February 15, 2026 14:50
Add entry translation feature using Google Translate (deep-translator).
Translates articles in bilingual interleave mode - each paragraph gets
its translated counterpart inserted below with distinct styling.

Backend:
- EntryTranslation model, TranslationService, and API endpoints
- Worker task with HTML-aware bilingual translation (BeautifulSoup)
- Auto-detect target language (Chinese ↔ English)
- Batch translation with chunk splitting for long content

Frontend:
- useEntryTranslation hook with polling for async results
- Bilingual display in ArticleReader with amber-styled translations
- i18n strings for en and zh-CN

Tests:
- 14 translation service unit tests
- 18 worker task unit tests (bilingual HTML, chunking, skip ancestors)
- 11 API integration tests

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace worker-based whole-article translation with frontend-driven
viewport translation. Only visible content (+ 500px buffer) is
translated via IntersectionObserver, with sentence-by-sentence bilingual
display beneath each paragraph.

- Add sync POST /entries/translate-texts API endpoint using Google
  Translate with batched ||| separator technique
- Add useViewportTranslation hook with IntersectionObserver, in-memory
  cache, and debounced batch translation
- Add sentence splitter (English/Chinese punctuation) and language
  detector utilities
- Update ArticleReader to use new viewport translation hook
- Add CSS for .glean-translation-line with fade-in animation
- Fix NotValidLength bug in worker translation batch fallback
- Add entry_translations migration

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

Persist sentence-level translations in a new paragraph_translations JSONB
column so repeated viewport scrolls hit DB cache instead of Google Translate.
Switch frontend from appending translation divs to in-place bilingual
replacement (Immersive Translate style) with restore-on-deactivate support.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…e/DeepL/OpenAI)

Allow users to choose their translation provider and API key in Settings.
Introduces a provider abstraction layer with Google Free (default), DeepL,
and OpenAI backends. No key = Google free fallback. Settings are stored in
the existing User.settings JSONB field and flow through both the sync
viewport translation endpoint and the async worker task.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Select component's onValueChange passes `string | null`, but
setModel expects `string`. Guard against null to fix TS2322 build error.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove unused _deactivateTranslation variable (ESLint)
- Add strict=True to zip() call (Ruff B905)
- Replace for loop with any() in _has_skip_ancestor (Ruff SIM110)
- Rewrite worker tests to use mock provider instead of GoogleTranslator
- Fix enqueue_job assertion to include user_id parameter
- Use @pytest_asyncio.fixture for async test fixtures

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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