Skip to content

feat: hybrid on-device + LibreTranslate note translation (closes #1055)#1095

Open
cornerblue wants to merge 8 commits into
PrimalHQ:mainfrom
cornerblue:bounty/1055-libretranslate-note-translation
Open

feat: hybrid on-device + LibreTranslate note translation (closes #1055)#1095
cornerblue wants to merge 8 commits into
PrimalHQ:mainfrom
cornerblue:bounty/1055-libretranslate-note-translation

Conversation

@cornerblue

@cornerblue cornerblue commented Jul 21, 2026

Copy link
Copy Markdown

Summary

Inline note translation for #1055 with a hybrid pipeline:

  1. On-device first (API 31+): Android TranslationManager / language detection — private, no network when models are available
  2. LibreTranslate fallback: when on-device is unavailable or fails (older OS, missing language packs)
  3. Token sanitizer (both paths): protects nostr: / bech32 (incl. nrelay) / URLs / invoices / bolt12 (lno1) / LNURL / cashu / lightning: / bc1 / hashtags / @mentions / shortcodes; restores ASCII-mangled placeholders
  4. Settings (Content Display): enable toggle, LibreTranslate base URL, optional API key, target language
  5. Endpoint normalize: base URL or full /translate path (no double path)
  6. Session cache: in-process LRU for network translations
  7. Offer gating: hide Translate on short / non-letter / token-only notes
  8. Already-in-target: calm info state (not a red error)

Why this PR

Compared with open #1094 (on-device only):

This PR (#1095) #1094
Pre-API 31 devices LibreTranslate fallback Hidden
Missing language packs Network fallback Unavailable
LibreTranslate / self-host Settings + client No
Token safety (invoices, npubs, cashu, bc1) Both paths None (raw text to TranslationManager)
Issue options (API key / self-host) Covered On-device only
Unit tests for sanitizer Yes Controls smoke only

Matches the issue's preferred approaches: on-device privacy when possible, plus self-hosted LibreTranslate and identifier safety so Lightning invoices and npubs are not mangled.

Test plan

  • Unit tests for NoteTextSanitizer (round-trip, bc1, nrelay, mentions, bolt12/lnurl/cashu, mangled placeholders, offer rules, endpoint normalize)
  • Manual: Translate on API 31+ with on-device packs → caption "Translated on this device"; npub/https/bc1 intact
  • Manual: Force fallback (unsupported language / pre-S emulator) → LibreTranslate path
  • Manual: Content Display → Note translation settings; paste full /translate URL and confirm normalize
  • Manual: Short / token-only notes do not show Translate
  • Manual: Already-in-target shows info (not red error)

Closes #1055

close PrimalHQ#1055

- Translate control under note body
- Token sanitizer preserves nostr:/URLs/hashtags/ln invoices/emoji shortcodes
- LibreTranslate HTTP client (OkHttp); target = device locale language
- Unit tests for NoteTextSanitizer protect/restore round-trip

Differentiated from on-device-only approaches: BYO/public LibreTranslate path
aligned with the issue's self-hosted provider option.
@cornerblue

Copy link
Copy Markdown
Author

Ready for review: LibreTranslate path + token sanitizer for #1055. Happy to add settings UI for base URL/API key or fall back to system TranslationManager if preferred.

Hybrid path for PrimalHQ#1055: try Android TranslationManager (API 31+) first
for privacy/offline, then fall back to LibreTranslate with nostr/URL
token sanitizer. Differentiates from on-device-only and network-only PRs.
@cornerblue

Copy link
Copy Markdown
Author

Update: hybrid translation

Pushed a hybrid path that should be strictly more complete than on-device-only or LibreTranslate-only alternatives:

  1. On-device Android TranslationManager first (API 31+)
  2. LibreTranslate + token sanitizer fallback when on-device is unavailable

Happy to drop either path if maintainers prefer a single strategy.

Align Android sanitizer with web client: keep bc1 outputs intact through
LibreTranslate and cover nrelay/bc1 restore paths in unit tests.
@cornerblue

Copy link
Copy Markdown
Author

Sanitizer alignment with web

  • Protects bc1… addresses (and existing nostr/lnbc/urls/hashtags/shortcodes)
  • Unit tests cover ordered tokens, bracket-mangling restore, and nrelay1…

Hybrid path remains: on-device TranslationManager first, LibreTranslate fallback.

@cornerblue cornerblue changed the title feat: inline LibreTranslate note translation (closes #1055) feat: hybrid on-device + LibreTranslate note translation (closes #1055) Jul 21, 2026
Add SharedPreferences-backed LibreTranslate settings and surface them under
Content Display. Wire the hybrid translator to use configured base URL, API
key, target language, and enable toggle.
Hide Translate for short non-letter notes, protect @mentions, and cache
LibreTranslate results in-process so hybrid on-device/network path stays
snappy. Expand sanitizer unit tests for mentions and offer rules.
@cornerblue

Copy link
Copy Markdown
Author

Update: cache + offer/sanitize polish

  • Session LRU cache for LibreTranslate results
  • Hide Translate on short / non-letter notes
  • Sanitizer also protects @mentions (plus existing bc1/nrelay/nostr/urls/invoices/hashtags/shortcodes)
  • Extra unit tests for mentions and offer rules

Hybrid path unchanged: on-device TranslationManager first, LibreTranslate fallback with settings.

Align Android note translation token protection with web client coverage
and add unit tests for offer/URI/cashu round-trips.
Protect Nostr/URL/invoice tokens before Android TranslationManager runs so
identifiers are not mangled, and restore them after. Adds a unit test for
ASCII-bracket mangling that on-device providers often produce.
- Accept base URL or full /translate path without double path
- Hide Translate when only protected tokens remain
- Treat already-in-target as info, not a red error
- Expand unit coverage for endpoint normalize and token-only notes
@cornerblue

Copy link
Copy Markdown
Author

Update: LibreTranslate base URL normalize, token-only offer gating, already-in-target as info (not error), extra unit tests.

@cortexuvula

Copy link
Copy Markdown

Reviewed this PR as the issue author. This is the strongest submission for the translation bounty on Android:

What stands out:

  • Hybrid approach: on-device TranslationManager (API 31+) as primary, LibreTranslate as fallback — best of both worlds for privacy and flexibility
  • Entity protection: NoteTextSanitizer handles nostr: refs, URLs, hashtags, lnbc invoices
  • LRU in-memory cache
  • Unit tests for sanitizer
  • Compose UI integration following existing Primal patterns
  • +840/-0 across 9 files

This supersedes #1068 (sato820). The hybrid on-device + LibreTranslate approach is the right design.

@miljan — requesting maintainer review. Issue #1055 has been open since Jun 23 with active community contributions.

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.

Feature: Translate notes to user's preferred language

2 participants