Entry #19
2026-10-19
9 min read
Translating apps is almost a must
Starting in English
Normally I build my apps as MVPs. When it's pretty much just an idea, it shouldn't take more than 7 to 10 days, including screenshots and a landing page.
We all know the drill. The main goal is to validate, and since almost every major app defaults to English, there's no reason not to use it for that too.
I'm not a native English speaker, but most apps, if they follow a good mental model, don't need much text. When they do, whether it's a description or a popup, double-checking with a translator or with AI is as simple as you would have wished years ago.
Apart from Hanni: Aprende Coreano, all my other apps were first designed in English. Even Hanni, which launched with two languages and was mostly made for Spanish speakers, has English as its default language.
Why Spanish comes second
According to a study on the 12 most frequently supported languages besides English, of the apps that offer a second language, 56.6% include Spanish, and French comes next at 47.6%. Many other analyses also put French at the top, especially for apps that convert well and make good profit: 10 most popular languages for localization.
Unfortunately, I don't speak French, but I'm a native Spanish speaker. So once an app feels like there's room to grow, and especially when it justifies the time, there's no reason not to add Spanish.
Structuring for languages from day one
From the start, even on an MVP, my apps are designed and structured to work with different languages. In React Native, that means I add i18n translations, wire the UI to them, and begin with a JSON file containing only English.
Going from one language to five is then a matter of translating the app into those new languages. The first app I translated was Split Workout Tracker, around version 1.0.3. As a learning exercise, I decided to add French, German, Italian, and of course Spanish.
Two ways to translate
Depending on the app and its size, I take different approaches to translation. They can be grouped into two, and they've changed a lot since the AI era.
Big projects with teams
Normally, for big projects with teams, I keep all translations in their own Excel file. It starts with two columns: the key, and the language (initially English). Then a parser in Google Colab reads that file and creates the JSON files with all the keys. Each column becomes its own file, named after the column: en, es, it, fr, and so on.
When there's heavier context, like exercise names, descriptions, groups, and tags in Split, or especially in Hanni, which I knew from the start would eventually support more languages, I split things into separate sheets. For Hanni, that means words and sentences for all the Korean lessons. The lessons and the learning path themselves are human-readable documents that can be parsed easily.
Solo projects and the React Native UI
Translating the UI in React Native is what has changed the most for me. Working alone and wanting to ship fast, I don't keep a sheet for the UI. Once I'm happy with the content, meaning button names, descriptions, and so on, I ask Claude or another AI to generate the JSON file for me, grouped by page and sometimes by subsection. Then I replace the strings in the code with the keys. When I create new files later, I use useTranslation and start with English, already using keys in most cases.
In the case of Split, it looks something like this:
"navigation": {
"settings": "Settings",
"home": "Home",
"guide": "Guide",
"moreApps": "More Apps",
"stats": "Stats"
},
"global": {
"saveChanges": "Save Changes",
"ok": "OK",
"add": "add",
"save": "Save",
"confirm": "confirm",
"reset": "Reset",
"lastTime": "Last time",
"sets": "sets",
"reps": "reps",
"time": "time",
"times": "times"
},
"routine": {
"routine": "Routine",
"day": "Day",
"addDay": "Add Day",
"editDay": "Edit Day",
"deleteDay": "Delete Day",
"expandAll": "Expand All",
"collapseAll": "Collapse All",
"dayLabel": "Day Label",
"dayType": "Day Type",
"noRoutine": "No routine yet",
"createFirstRoutine": "Create your first routine to get started",
"dayTypeCannotChange": "Day type cannot be changed"
}
Then, when I'm ready to add another language, I share the JSON file with Claude and ask for the translation.
"navigation": {
"settings": "Configuración",
"home": "Inicio",
"guide": "Guía",
"moreApps": "Otras aplicaciones",
"stats": "Estadísticas"
},
"global": {
"saveChanges": "Guardar cambios",
"ok": "OK",
"add": "Añadir",
"save": "Guardar",
"confirm": "Confirmar",
"reset": "Restablecer",
"lastTime": "Última vez",
"sets": "series",
"reps": "reps",
"time": "vez",
"times": "veces"
},
"routine": {
"routine": "Rutina",
"day": "Día",
"addDay": "Añadir Día",
"editDay": "Editar Día",
"deleteDay": "Eliminar Día",
"expandAll": "Expandir Todo",
"collapseAll": "Contraer Todo",
"dayLabel": "Nombre del Día",
"dayTypeCannotChange": "El tipo de día no se puede cambiar",
"dayType": "Tipo de Día",
"noRoutine": "Aún no hay rutinas",
"createFirstRoutine": "Crea tu primera rutina para empezar"
}
Checking the translations
Spanish is easy to check. Portuguese isn't that hard either, but with French and especially German, I have to trust the translation, since I don't really know those languages.
On Split, I took the bigger risk by adding five languages. It's a gym app, and most of the terms are simple, so I didn't mind if it wasn't 100% accurate, especially since the app is free. The real work, and the real need for trust, came from the exercise data, since names matter more. That's why, under the language switcher in the app, I added a Google Form where users can suggest a translation if they spot an error.
Split's exercise data
Split is fully local, so the exercises JSON lives inside the app too. It looks like this:
{
"id": "abductor_machine",
"name": {
"en": "Hip Abduction Machine",
"es": "Máquina de Abducción",
"pt": "Máquina de abdução do quadril",
"fr": "Machine d'abduction de la hanche",
"de": "Hüftabduktionsmaschine",
"it": "Macchina per l'abduzione dell'anca"
},
"type": "legs",
"tags": ["glutes", "abductors", "isolation", "machine"],
"primaryMuscles": ["glutes"],
"secondaryMuscles": [""],
"equipment": "machine",
"difficulty": "beginner",
"description": {
"en": "Push legs apart using machine.",
"es": "Separa las piernas usando máquina.",
"pt": "Afastar as pernas usando a máquina.",
"fr": "Écartez les jambes à l'aide de la machine.",
"de": "Beine mit Hilfe einer Maschine auseinanderdrücken.",
"it": "Allargare le gambe con l'ausilio di un attrezzo."
}
}
Since I'm not sure about the descriptions, at the moment I only use the names. The app is meant to be simple, and I don't think descriptions would add much value to Split.
Hanni: Learn Korean
Hanni was created as a project of Aprende Coreano Paso a Paso, which already has a large community of Spanish speakers. But the app was built from zero to support as many languages as possible.
So the app has many different sheets that need careful translation: words, sentences, locations, explanations, and more.
You can read more about Hanni in this post "Eight years of teaching Korean".
For now, the app is only available in Spanish and English. Hopefully, once we have Portuguese speakers who can translate or check the translations, we'll release it in Portuguese, and eventually in other languages too.
Translating Swift apps
When it comes to native iOS apps built with SwiftUI, translation is incredibly simple. If your project is well structured from the beginning, adding a String Catalog does most of the initial work. From there, in most cases, I only share the file with Claude and get the translation right.
A final inspection is usually needed. Some parts of the app need the type of their strings changed so the catalog can pick them up, especially when using enums.
Meleve, the first one
Meleve was the first Swift app I translated. It's a minimalist app, so there wasn't much to translate. If I'm not mistaken, there were fewer than 60 words and sentences. That's why I decided to add Spanish and Portuguese. Even though I don't speak Portuguese, it's fairly easy to understand, and I had some Brazilian friends to double-check it.
Three more apps in Spanish
Last week I decided to add translations to three of my other apps, the simpler ones. It really only took less than a day.
The three apps are Feelino, Tabs, and Merame. They're now available in Spanish. The translation itself took half a day, and then the afternoon went to screenshots, the app title, the description, and the other small App Store tasks related to translation.
The screenshots took the most time, even though they're very simple. I design my own screenshots in Figma, so I had to translate them for mobile and tablet and upload them to the App Store.
Of the three, Merame stood out, since it's one of my best-performing apps in the store. But since translating them was really that simple, there wasn't a real reason not to do all of them.
It also gave me the chance to revisit the other two apps. I changed a few things and even improved the screenshots, English included, to see if they could perform better and be discovered more. Feelino does fairly well and has good acceptance, but it's more or less invisible in the store.
What's next
The next updates will bring a new language and some fine-tuning, as always, trying to optimize visibility. But I'm prioritizing based on how much work each one takes compared to the expected payoff, especially for newer apps.
Looking back
What used to feel like a big project is now one of the simplest things I do for an app. The key is structure from the start. If the code is ready for languages, adding one is mostly translation and patience. And when it's a language you know, or one your friends can check, it's even easier to trust what you're shipping.