<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: levit</title>
    <description>The latest articles on DEV Community by levit (@yousurm).</description>
    <link>https://dev.to/yousurm</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4220100%2Fb2301c65-2859-4109-8b0d-31b4cb13eeb0.jpg</url>
      <title>DEV Community: levit</title>
      <link>https://dev.to/yousurm</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/yousurm"/>
    <language>en</language>
    <item>
      <title>I got tired of copying text back and forth to translate it. Building the Mac workflow was the harder part.</title>
      <dc:creator>levit</dc:creator>
      <pubDate>Sun, 01 Nov 2026 08:14:30 +0000</pubDate>
      <link>https://dev.to/yousurm/i-got-tired-of-copying-text-back-and-forth-to-translate-it-building-the-mac-workflow-was-the-57cl</link>
      <guid>https://dev.to/yousurm/i-got-tired-of-copying-text-back-and-forth-to-translate-it-building-the-mac-workflow-was-the-57cl</guid>
      <description>&lt;p&gt;I often need to translate text while working on my Mac, and one thing that always bothered me was the workflow.&lt;br&gt;
Select some text. Copy it. Open a translator. Paste it. Get the result. Copy it back.&lt;br&gt;
It’s not a big deal once or twice. Doing it repeatedly throughout the day gets annoying.&lt;br&gt;
So I started building a small native Mac app.&lt;br&gt;
The initial idea was simple: select text, press a shortcut, and see a translation without switching apps.&lt;br&gt;
Then I tried to make that work with actual drafts, formatting, changing focus, and voice input.&lt;br&gt;
That’s where most of the work ended up.&lt;/p&gt;

&lt;h1&gt;
  
  
  A shortcut needs to know what the user is doing
&lt;/h1&gt;

&lt;p&gt;I settled on &lt;strong&gt;double-tap ⌘&lt;/strong&gt;.&lt;br&gt;
With text selected, it opens a translation preview. Without a selection, it opens voice input.&lt;br&gt;
That sounds straightforward, but those two situations need different handling.&lt;br&gt;
A readable selection isn’t necessarily editable. A paragraph on a webpage and a draft in an editor can both be selected, but only one may support replacement.&lt;br&gt;
I had to keep those questions separate: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Can the app read the selection?&lt;/li&gt;
&lt;li&gt;Can it identify the original editing context?&lt;/li&gt;
&lt;li&gt;Can it safely insert or replace text there?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;⠀Treating “I captured some text” as permission to replace it would have been a mistake.&lt;/p&gt;

&lt;h1&gt;
  
  
  Previewing and replacing should be separate actions
&lt;/h1&gt;

&lt;p&gt;For translation, I wanted the original draft to stay unchanged while the result was being generated.&lt;br&gt;
The flow became:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Capture the selected text and its context.&lt;/li&gt;
&lt;li&gt;Show the translation.&lt;/li&gt;
&lt;li&gt;Let the user review it.&lt;/li&gt;
&lt;li&gt;Replace the selection only after confirmation.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;⠀That extra decision matters when the text includes a deadline, a quantity, or a condition.&lt;br&gt;
For example:&lt;br&gt;
I can send the first draft on Friday. Final delivery depends on the revisions.&lt;br&gt;
A fluent translation can still change the commitment. The interface needs to leave room for checking it.&lt;br&gt;
A preview doesn’t prove the translation is correct. It gives the user a chance to catch a problem before changing the draft.&lt;/p&gt;

&lt;h1&gt;
  
  
  Text replacement is more than sending a paste command
&lt;/h1&gt;

&lt;p&gt;One of the less obvious problems was deciding whether insertion had actually succeeded.&lt;br&gt;
An editor may normalize paragraphs or represent formatting differently. Comparing every output character and formatting detail too rigidly can report a failure even when the text has arrived.&lt;br&gt;
But assuming success immediately after a paste is also unsafe.&lt;br&gt;
The latest work focused on checking for a change in the original editing context, with the caret in the expected position and the surrounding text unchanged.&lt;br&gt;
When the result is uncertain, the app needs a recovery path. It shouldn’t keep inserting the same text and hope that one attempt works.&lt;/p&gt;

&lt;h1&gt;
  
  
  A hyperlink is more than its visible label
&lt;/h1&gt;

&lt;p&gt;I also wanted to preserve basic emphasis and actual web links.&lt;br&gt;
Bold and italic text carry useful information. A link has an additional constraint: its visible text can be translated, but its destination should remain the original destination.&lt;br&gt;
I didn’t want a translation model to reconstruct URLs.&lt;br&gt;
The approach I used was to keep the destinations locally and send opaque, per-operation references through the translation request. Returned references are checked against those captured from the source.&lt;br&gt;
An unknown reference is discarded instead of being turned into a guessed link.&lt;br&gt;
This also keeps the distinction between real rich text and Markdown clear. Text that looks like Markdown isn’t automatically treated as a hyperlink.&lt;br&gt;
It’s a small feature from the user’s perspective. Underneath, it needs a fairly strict contract.&lt;/p&gt;

&lt;h1&gt;
  
  
  Voice input added another review step
&lt;/h1&gt;

&lt;p&gt;Later, I added dictation.&lt;br&gt;
With no text selected, &lt;strong&gt;double-tap ⌘&lt;/strong&gt; opens voice input. The user can speak, watch the transcript appear, and review the final text.&lt;br&gt;
From there, they can use the original transcript or translate it before insertion.&lt;br&gt;
I keep those stages explicit:&lt;br&gt;
Speech → transcript → optional translation → insertion&lt;br&gt;
Recognizing what someone said and translating it are different operations. If the transcript is already wrong, translating it can make the error harder to notice.&lt;br&gt;
The spoken source language also shouldn’t be inferred from the translation target. Wanting English output doesn’t mean the user is speaking English.&lt;/p&gt;

&lt;h1&gt;
  
  
  Native UI doesn’t mean everything runs locally
&lt;/h1&gt;

&lt;p&gt;The app uses Swift, AppKit, and SwiftUI. Speech capture uses AVAudioEngine, with streaming transcription through Soniox. Translation uses Apple’s system Translation framework or Gemini, depending on the selected engine.&lt;br&gt;
These paths have different requirements.&lt;br&gt;
Apple translation depends on the macOS version and supported language models. Streaming dictation and Gemini translation require an internet connection.&lt;br&gt;
That distinction needs to be visible to users. A native Mac interface isn’t a reason to describe cloud processing as on-device processing.&lt;/p&gt;

&lt;h1&gt;
  
  
  What I’d pay attention to earlier next time
&lt;/h1&gt;

&lt;p&gt;I started with the translation request. I’d spend more time on the boundaries earlier now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Selection capture and editability are separate.&lt;/li&gt;
&lt;li&gt;Focus can change while an asynchronous request is running.&lt;/li&gt;
&lt;li&gt;Formatting support depends on both the source and destination.&lt;/li&gt;
&lt;li&gt;Insertion needs verification and a recovery path.&lt;/li&gt;
&lt;li&gt;Transcription and translation need separate review.&lt;/li&gt;
&lt;li&gt;Basic formatting preservation isn’t a promise to preserve a whole document layout.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;⠀The original idea still feels small to me: make translating a sentence less frustrating.&lt;br&gt;
Most of the engineering has gone into making the steps around that sentence predictable.&lt;br&gt;
If you’ve worked on cross-app text insertion on macOS, I’d be interested in how you handle editors that normalize text after insertion.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Writing disclosure: AI tools assisted with drafting and English editing.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>swift</category>
      <category>xamarin</category>
      <category>ai</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
