Most translation tools make you leave what you're doing: copy text, switch to a tab or app, paste, read, copy back. I wanted translation to sit inside whatever I was already using. That turned into Linguin, an AI translator with a native Mac app, an iPhone app, and a Chrome extension. Here's how I think about building the "same" product across three very different surfaces.
One idea, three surfaces
The product promise is identical everywhere: select text, get a fast, context-aware translation in 100+ languages. But the interaction has to be native to each platform, or it feels wrong:
- macOS: a menu-bar app plus a global hotkey. Select text anywhere, hit the shortcut, get an overlay. The whole point is that you never switch apps.
- iOS: a share-sheet extension and keyboard, so translation is one tap from any app instead of a context switch to a separate screen.
- Chrome: an extension that translates selections and full pages in place, keeping layout intact.
Same core, three genuinely different UX shells.
Keeping the core in one place
The translation logic - language detection, prompt construction, tone handling, caching - is the part you never want to fork. If Mac and iOS drift, you get subtly different translations for the same input, which erodes trust fast.
So the model-facing logic lives behind one small service contract, and each client is a thin shell that owns only its platform UX. A translation request looks the same whether it comes from the menu bar, the share sheet, or a browser selection.
Why "AI" translation instead of a classic API
Classic MT is great at literal accuracy but blind to context. "Book" in a travel app and "book" in a library app should not translate the same way. Passing the surrounding context and a tone hint (formal, casual, technical) to an LLM produces translations that read like a human wrote them, not a dictionary.
The tradeoffs I had to design around:
- Latency: users expect near-instant results, so caching and streaming matter a lot.
- Consistency: same input should give the same output, so requests are normalized and cached aggressively.
- Cost: batching and caching keep per-translation cost sane.
Lessons so far
- Ship the platform-native interaction first. A technically perfect translation behind a clunky flow loses to a decent translation that's one keystroke away.
- Centralize the model logic early, before the clients diverge.
- Context and tone are where AI translation actually beats the old tools - lean into that, not raw language count.
If you want to try it, the Mac app, iOS app and Chrome extension are all at https://linguin.app
Curious how others structure a shared core across native + web clients - do you go with a shared service, a shared package, or just accept some duplication?
Top comments (0)