The Localization of Krumit’s Tale
I’ve gotten a few questions about localization in Krumit’s Tale, so I wanted to write up a bit about how it was done.
I’ll talk a bit about the tools used, the impact on sales, and finally, some of the challenges I ran into with the localization.
I wanted to start off by talking about Localizor, which was an essential part of getting Krumit’s Tale localized in 7 different languages (so far!)
Prior to using Localizor, I’d used Google Sheets to organize the translations for Chinese. It worked ok but it quickly became a mess as I iterated and tweaked the balance of the game (requiring string changes). The translators I was working with constantly got confused since it wasn’t clear which strings had been updated, and how. I tried various methods to mark changed strings, but even that quickly became a mess, especially when I was working across multiple branches.
It was around this time that I discovered Localizor based on a tweet from the developers of Nowhere Prophet. It was a new tool for handling localization in games. What made it really unique was that it was a distributed platform for translation — anyone could help contribute and the best translations were voted up and added to the game.
I quickly found an amazing community of translators on Localizor, who were often players of Krumit’s Tale (or similar games) that wanted to help bring the game to new languages. I was really impressed at the level of engagement I got from the translators, who put a lot of care into make sure they captured the nuance and intent of the source text.
Inspired by the distributed translation model of Slay the Spire and their #localization Discord, I set up a similar channel on my own Discord server to help organize and coordinate the translators. I used Localizor to help notify them when new translations were available, and explained the changes in more detail / answered questions on my Discord.
Besides connecting me with translators across many different languages, Localizor also solved the problem of keeping track of changed strings. When I uploaded strings, Localizor would flag strings that had changed, and I could request a re-translation of those strings.
The distributed, ‘process light’ nature and content management capabilities of Localizor made it the perfect fit for Krumit’s Tale. If you’re an indie dev that’s interested in bringing your game to a broad audience, Localizor is an indispensable tool to help you to do that.
What about professional translations?
An alternative to Localizor would be to request translations from a professional translation studio. I’ve done so in the past, both for personal and professional projects.
There were a couple aspects of Localizor that I preferred, especially for a small & agile project:
- The work load is distributed across a community of people. If you’re often adding or tweaking strings, you can often turnaround the results more quickly than with a single localization studio
- ‘Wisdom of the Crowds’. Although I haven’t realized the full potential of this in my project, I like the ability of the community to vote for translations that are better than the current translation. Even professionals make mistakes, particularly with context, and this ‘open source’ models helps to fix it. This has been a big help in improving the translations for certain languages for Krumit’s Tale, especially Spanish.
- Better understanding of the game and underlying mechanics. Many translators on Localizor found and played Krumit’s Tale after finding it on Localizor, or joined Localizor to help translate it. As a result, they have a fantastic understanding of the game’s tone, rules, and mechanics that would be difficult to convey to a localization studio. I can also chat with them on the Meteorfall Discord about localization questions, which is not something I’d expect to be able to do with a professional studio.
- Lack of formal process to request translations. I update the source strings on Localizor and Localizor helps notify the community of translators about the update. No need to make a formal request to a studio or agency for an update. The whole process feels very lightweight.
There are certainly plenty of advantages to having a professional studio do the work instead. One issue I ran into on Krumit’s Tale for instance was translations from different translators could sometimes be inconsistent, which could lead to confusion in-game. Over time, these types of issues can be smoothed out but could be mitigated upfront by having a single person or studio doing the work.
Additionally, the quality of translations can vary. A user’s ‘reputation score’ can give a signal about the quality (or at least quantity) of that user’s contributions across various projects, but you have no way to determine a user’s fluency or quality. Without someone to double check the translations, it can be difficult to ensure accuracy, consistency, and quality. There’s an intangible price to be paid when translations are distributed and generally provided on a volunteer basis.
Overall, I think Localizor is a compelling choice for localization — especially for small devs — where the limited budget and agile nature of the project makes for a rough fit with a more formal localization process. I think there’s a ton of potential as well for professional studios to provide a similar lightweight process to small devs, using the Localizor platform as a way to provide it.
How Did Localization Impact Sales?
Localization can take a lot of time and effort to get right, so is it worth it the time and effort it takes?
Looking at a couple of examples —
- China sold less than 50 copies on Steam in February. In late March / Early April when we rolled out the Chinese translation, China sold ~250 copies in March and another 500 in April. It continues to be one of the top platforms overall.
- Russia and Japan sold less than 10 copies in total on Steam in June, and similar in the months leading up to it. The following month, in July, these two countries sold about 300 copies apiece for a total of more than 600 copies
- On iOS, Japan to date is the #2 top seller (behind the US) with about a quarter of the total US sales and more than the next 3 countries combined
Especially for countries where English literacy is less common, it can make a huge impact on sales. Not translating Krumit’s Tale would’ve meant leaving a significant portion of the total sales on the table, especially in countries like China, Russia, and Japan.
Challenges with Localization
Localization did present quite a few challenges as well.
One Font to Rule Them All?
One of the challenges I had to deal with was the fact that I didn’t have just one font that contained all the characters, and so it became a challenge figuring out how to show a different font for different languages. For awhile, I tried to use ‘fallback fonts’ in Unity, but realized later that it created a huge memory overhead since it loaded multiple very large texture atlases into memory.
What I ended up doing is adding a new MonoBehaviour to every text field that can swap out the font at runtime (I’m sure someone will probably tell me — ‘you idiot, there’s a better way!’)
There are two main fonts in Krumit’s Tale: “Krumit Bold”, a bold font used for titles and “Evgeny”, a lighter weight font used for longer descriptions. For each font, I have a look up table that maps the bold & normal font to the equivalent font for that language. The MonoBehaviour checks the base font of the attached TextMeshPro text field and loads the appropriate font for the currently selected language.
It was a pain to add this field to every text field, but once I’d done it, it made adding new fonts for any language pretty easy.
Type Safety for Strings
One must have for me when adding strings to my game is type safety. I don’t like referencing string ID’s via string literal and prefer to use something more structured. This ensures I don’t make a mistake when referring to a string by ID and also makes it trivial to find where a string is referenced.
All the strings in Krumit’s Tale are stored in StringResource.cs, which has a bunch of sub classes for organization (eg: StringResource.Items, StringResource.Monsters, etc… )
In code, I always refer to the string’s ID when it’s used by the StringResource path, which gives me compile time type safety when referencing strings. For instance, if I want to display an error message, I can just refer to StringResource.Errors.NotEnoughGold instead of a string literal which is easy to mess up.
I wrote a simple Unity tool (a menu option, really) which syncs the contents of StringResource.cs with the English string .json file. For instance, whenever I add a new key to StringResource, I run this tool to add that key to the English JSON so that I don’t have to remember to do it manually.
Finally, I use reflection to iterate over all the keys in StringResource.cs, pull the correct translation from the correct JSON based on the selected language, and then update the value of the key in StringResource.cs so that it’s correctly displayed whenever I request it.
Asian Text / Line Wrapping
The default UGUI text box doesn’t properly wrap lines written in Asian languages such as Chinese, Japanese, and Korean. I highly recommend using the built-in Text Mesh Pro text box instead, which properly handles Asian fonts and wraps them properly.
The Usual Issues
All the other typically mentioned localization issues — in particular text not fitting — were all present. Using ‘Best Fit’ on many of the text boxes became my best friend to help handle this type of issue.
Other Random Details
- All the strings in Krumit’s Tale are stored as JSON key-value pairs, which is what Localizor natively exports. It’s a really common and easy format to work with. I found it superior to .csv because it doesn’t really require thinking about escaping commas and it adds a bit more structure to the text