Internationalizing/localizing is hard. You have to comb through your whole app and look for what needs to be translated and manually update your translations every time new stuff gets added. People sometimes hire their own in-house translators, or they send off their translations to humans to be translated, which takes days, weeks, or worse and costs a ton. Internationalization is, of course, super important for user growth and retention, and often neglected until very late, when competitors will make your product for LATAM, APAC, or Europe.
I worked at Slack for three years on the Slack Connect team, building features like Shared Channels and Shared Workspaces, which had millions of users all over the world. Our translation system was slow and painful, often delaying product launches when things were added last-minute, and resulted in context-less and inconsistent translations. Words like "duplicate" could be either an action or a state, depending on if it's in a button or just displaying a status, and words like "Huddles" were translated ten different ways by translators who just wanted to get through as many translations as possible as quickly as possible, hindering adoption and confusing users.
In a parallel universe, but in the same Downtown Oakland apartment, Brendan worked at a retail startup from the founder of Woot, acquired by Amazon, and they brought someone in to manually translate their product into Spanish, which was expensive and took months. Even worse, the translations had to be manually ported over by engineers every time they updated.
Brendan and I have been coding together since fourth grade, starting by making Cydia tweaks on iOS 4, building apps like one of the first Dogecoin wallets (DogeTicker) in ninth grade, and running small businesses fixing computers and making websites. When we realized we both had miserable translation experiences, we decided to work together to make these approaches a thing of the past!
Our solution to these problems is multiple. Firstly, we utilize a custom Babel plugin to traverse the AST and search for user-facing strings that should be translated, eliminating the need to manually comb through the application and gather strings. During builds, we also utilize the AST to provide context to the LLMs on any new text which is then used to give correct translations for different use cases, (e.g. “Duplicate — is it in a button, where it’s a verb, or is in a regular string, where it’s a noun?)
After scanning for strings and wrapping them in functions to display correct translations to the user based on their browser locale, we gather these strings and build time, and submit new strings to be translated and bring in recently translated strings. During translation, we take both the context and similar strings to guarantee consistency. Translation itself using our LLMs only takes a few seconds, rather than days utilizing humans. Of course, we check them afterwards, but LLMs were themselves built for translation and semantic understanding, so they are incredibly good at delivering context-rich, consistent, and great translations better than humans are.
What's great about this solution is that translations are instant and excellent, and you don't have to spend hundreds of hours finding what needs to be translated and passing them off to an external team, you don't have to wait days for bad quality translations, you don't have to maintain a bot to merge completed translations into the codebase, and you don't have to pass context yourself.
We've got a pilot running on our site right now, so if you've got a Next.js project, feel free to try it out using the script on our homepage: https://getquetzal.com. If you've got a project written in some other framework, like vanilla React or React Native, reach out to us (founders@getquetzal.com), we've got npm packages that support these too. If you'd like to see support for something else entirely, like Swift, please let us know and we'll move it up on our docket.
Let me know what you all think about how software translation is today. Is it hard? What approaches have worked or not? We also want to explore problems like how people reach new markets and find customers, so that’s something we’re thinking a lot about as well. If you’ve got funny bad translation stories, I’d love to hear those too, the more shocking and hilarious the better.
Of course, we all know that this is very rarely how projects end up getting setup especially in the early stages, and then it's just massive amounts of work to go back and set it up later.
The thing that's the most intriguing to me about what you're describing is automatically setting up translations in the build step where you auto-detect strings to translate. But looking at the site, most of it seems to be focused around the VSCode extension which will just sort of find and replace strings in the source code with t() tags.
Can you talk more about the translations in the build step? Is there a reason you're not talking more about that on the site? (Is it just newer, not very reliable/good, or...)?
The idea that I could just throw something like this into my project, not have t() tags in my source code but still get translations, sounds like magic and I think it would be really neat.
So, for the build time translation-
Yeah, I would say it's not reliable yet =p
But, it's not that far off. It's not magic- the idea is that we inject the t functions at build time so that they don't need to be in your code. The vscode extension is a good visual for this- for many patterns, it correctly notices what does or doesn't need to be translated.
But, the real problem is that if a process like this goes awry (a strange pattern leads to text being erroneously translated/untranslated) then it is next to impossible for someone to debug.
Glad you think this is cool. We think this is absolutely on the horizon, and we hope to be the first to get people using it... but in the meantime, we don't want to be responsible for issues on prod...
That's the correct thing to do. When you're first building a product you're trying to prove that it can and should exist: that you can get people to pay you money for it. It doesn't make sense to spend your time or money doing anything else.
Once you're successful then you know that people in one country like your product and maybe people in another company will as well. But until you know that, that's the only thing you should be working on.
User-visible strings are consistently translatable, and the translation mechanism needs to have deep access in the language for this. I think in typescript this is a fairly doable thing given the AST access you yourself make use of. I'll gladly dig into how you do this on your end but I'm guessing it's somewhere along those lines but not quite?
Incidentally, when you have two string types, it becomes fairly straightforward to detect strings that probably should be translated but aren't tagged as such. Most strings are like this, in fact, because string constants tend to be a bad idea in general (vs numeric constants), and string operations tend to be a bad idea in the context of i18n (you want to use templated strings instead). So you tag until you only have a few left-over.
The upside of this approach is that we get a lot of context for accurate translation. The other upside is that down the line we can pull off fully automatic translation, but as others have pointed out, this is more of a gimmick. We think it's cool but it's more like the cherry on top
Also, yeah, that pattern would make life infinitely easier. Most develors really should think like this already, and not mix user facing strings with strings for other logic. But from what ive seen, pre i18n, devs dont think like this. Someday...
Any plans to extend this to iOS/Android development in the future? I assume it would already be easy to integrate this into React Native.
Also, is there a way for me to provide explicit additional context to the `t` function for the translation? Essentially a string that is appended to the LLM input for translation. For example, in Japanese there is often a significant difference between formal and informal language, and it is common to add post-positional particles such as や, が, and の to make titles and labels sound more natural. I see you have addressed many other special cases around numbers/dates/etc, so certain flags like formal/informal, regional dialect, etc may be valuable future additions.
Overall looks really nice and I look forward to trying this the next time the need arises.
Does your result live update the strings in place if the device locale is changed?
Do you have any method for getting feedback from UI tests? I don’t now, but that is absolutely a feature I was used to previously. We used to OCR off expected areas to ensure things fit etc.
As for the UI/UX tests, we're looking to integrate with PostHog and work with another company in our batch (PathPilot) which focus on analyzing user experience
Once we work these analytics in, the theory is we can better predict UI issues and resolve them retroactively. A full UI testing suite is something people have asked for, and we aren't there yet, but the tools are here
The classic always used to be German overflows, but these days the wrong sort of Chinese and RTL is more of a headache. (I have been ignoring proper RTL for a while).
I have also noticed LLMs get stuck in healthy/unhealthy loops when doing this sort of work. If you can snapshot the state of them when they are doing the right thing it would be very useful. They also build a lot of good per app context which improves the result quality.
If you've got a big codebase, the VSCode extension can be super helpful for setting up.
It's a huge pet peeve of mine when translation services aren't themselves fully translated, and I see it a surprising amount ahaha
No pricing to be found in the header for an AI product (which you'd expect to be on the pricier side) isn't great either.
- "oder siehe So funktioniert es" should be worded differently, it seems to be two different strings
- "Ingenieure Zeichenketten in Schalter einwickeln" (From about page - I guess Zeichenketten is supposed to mean strings, which I think is more natural just leaving it untranslated, the sentence literally translates to "engineers embed strings/text into switches")
- the translated texts have quite long and complicated sentences, which feel unnatural (for example in the subtitle)
About pricing: I get it's early, though I think it'd probably be useful for users to have an idea of how much it might cost in the future, if they actually want to use it in a project.
The navigation also seems to overlap with the title on Chrome (latest Android)
The idea of parsing source code to auto inject translations, especially while leveraging machine translations comes up every 2 months.
It’s not solving the problem.
The problem to be solved is change control. Doing translations is (surprise!) cheap compared to controlling changes. Changes referring to the marketing copy changed, the button label changed, a new screen has been added, etc. It needs one system that can track and control changes across apps, translations, files.
If change control is solved, localization boils down to managing CI/CD pipelines.