Voice notes are everywhere on WhatsApp, but they're a nightmare when they're in a language you don't speak. You can't copy them into a translator, and even if you understand them, you still have to reply.
So I built ReplyLingo: share a voice message to the app, read it in your language, then record a reply in your language and get a voice message back in theirs.
I'm a solo developer from India. Here's how it works under the hood and what I learned shipping it to the App Store and Google Play.
The flow
User long-presses a voice note in WhatsApp / Telegram / iMessage and taps Share.
The app receives the audio file through the share sheet / share intent.
The backend transcribes it, translates it and returns the text.
The user taps reply and speaks in their own language.
The backend transcribes, translates and generates speech in the other person's language.
The user shares the generated voice note back to the chat.
The stack
App: React Native with Expo (EAS builds, not Expo Go, because share intents and native modules need a dev build)
Auth: Firebase Authentication (Google, Apple, plus guest mode)
Payments: RevenueCat for subscriptions on both stores
Backend: Node.js on Vercel, MongoDB
Speech to text: OpenAI Whisper with automatic language detection
Translation: an OpenAI chat model
Text to speech: Google Cloud TTS (Chirp 3 HD voices where available)
Lessons learned
- Receiving shared audio is the hardest part
WhatsApp shares voice notes as .opus/.ogg on Android and different formats on iOS. You get content:// URIs that need to be read before permissions expire, and some Android skins (looking at you, MIUI) restrict apps in the background. Test the share flow on as many phones as you can, with the app both open and closed.
- Serverless limits bite
Vercel functions have a request/response body limit of about 4.5 MB. A long voice note recorded at high quality, or a long generated reply sent back as base64, can go over it. My fixes:
record replies at a lower bitrate (speech doesn't need music quality),
encode the generated MP3 at 64 kbps mono,
split long TTS text into chunks (Google TTS has a per-request byte limit) and join the audio.
Bonus lesson: the app was showing an "audio too long" error that was really a 413 from the platform because of file size. Log the HTTP status and file size with every failure, or you'll chase the wrong bug.
- Retries need idempotency
Mobile networks drop requests all the time. I added automatic retries with backoff, but a retry after the server had already finished meant users could be charged credits twice. The fix was a client-generated idempotency key sent with every upload, stored with a unique index on the server, so the same action is only charged once.
- Be honest about language support
"100+ languages" sounds great, but text-to-speech voices don't exist for every language. I keep one language list on the backend (served to the app) that marks each language as voice reply or text reply only, so users never get a broken audio file.
- Paywall timing matters more than paywall design
My first version showed the paywall too early, sometimes before users had even heard their result. Moving it to after the user has seen and played their translation made a big difference. Adding a 3-day free trial with a clear "we'll remind you 1 day before it ends" message helped Android users the most.
- AI search is a real acquisition channel
Most of my website traffic comes from people asking ChatGPT how to translate a WhatsApp voice message. Clear, factual pages that each answer one question (plus llms.txt and IndexNow for Bing) did more for me than social media videos.
Try it / feedback welcome
ReplyLingo is free to try on iPhone and Android: https://www.replylingo.com
If you've built anything with share intents, Whisper or TTS in React Native, I'd love to hear what tripped you up. And if you try the app, tell me what's missing!

Top comments (0)