I wanted to give myself a challenge, so I decided to teach myself how to localize games in Godot. Godot is a free, open-source game engine, and currently (3.0+), it uses three different languages: GDScript (its own language that is dynamically scripted, similar to Python), C#, and JSON. A single game can have all three.
On the surface, Godot localization is simple. It is integrated into the editor, and translation files are automatically generated from CSV or .mo/.po files. You can easily assign fonts, images, or sounds to certain languages by remapping them in your Project Settings. It has a built-in translation method (tr()). It even has a TranslationServer class to manage translations. But there are many different ways text can end up on the screen, and each of these requires a slightly different approach.
In the game I chose to localize into Russian, I encountered two of the three languages (GDScript and JSON), and four different localization strategies were required. The game I ended up experimenting with is Hijinx, which was developed by John Gabriel. Gabriel had posted his games on a Reddit post asking for people to offer up their games for people to use to help them learn the engine, and I chose it because it had a lot of text compared to other Godot games, which I figured would mean that there would be a wide variety of localization problems — and I was right.
Different Localization Scenarios
Static UI Text
The way Godot primarily works is that there are “labels” in the game, and you create a CSV in Excel with “keys” for the labels and the translations in all your languages (or .mo/.po files, if you want to take that route). Some of these labels are simply in the UI, and you can change the label directly in the .tscn (scene) file using the scene editor, in the 2D or 3D space. Then the corresponding key for the language the game is running in will be found during run time. There is no need to wrap strings or change anything in code. An example of this would be the copyright line on the main menu screen.
Static UI Text Requiring Player Interaction
Another variation on this theme is text in the UI that while it can be changed directly in the 2D/3D scene editor, it also has to be changed in the code itself in the .gd file that corresponds to the .tscn file (you can access the script by clicking on the scroll icon next to the name of the scene file in the scene menu, although sometimes you just have to search around for the necessary code), because the player will interact with it in some way (clicking on it, pressing the “return” key, etc.). An example of this would be any of the menus in the game. The label text in the game, which would just be a hard-coded string in a game that has not been internationalized, is simply changed to the key you have designated for the label.
Dynamically Changing Text in the UI
Sometimes, you will come across text that you can see in the Scene Editor, but you cannot change the label there – the text is determined by what is happening on the screen. I encountered this while localizing the map UI. The name of the level, for instance, displays at the bottom, depending on what location on the map your character is standing on. For this, I had to locate where the text is in the script files and change the hard-coded strings to the keys there.
Dynamically Changing Text in a .tscn File
There can also be text that does not appear in the scene editor (the designated location will be visible, but no text). In this game, there are tips that show to the user before they play a level. The hard-coded strings had to be changed to keys in the .tscn file itself. Interestingly, the concatenated string required wrapping using tr(), while the others did not.
Dynamically Changing Text in a JSON File
var loc = TranslationServer.get_locale() func load_conversation(): var file = File.new() if loc == "ru" : file.open("res://json/conversations_ru.tscn", file.READ) else: file.open("res://json/conversations.tscn", file.READ) var contents = parse_json(file.get_as_text()) conversation = contents["conversations"][conversation_slug] file.close()
Hijinx is a complex game, and one of the challenges of Godot localization that I found is that while there is so much built into Godot for localization, there is not a lot of information available on it. I ended up having to download Godot’s demo games and trying localize those, but once it clicked, then it did become as easy as the documentation promised. And I hope that this blog post will help fill in some of the gaps for the next person who decides to localize a Godot game!