Understanding localization strategies and edge cases

Godot Version

4.5 dev

Question

I’ve been digging into the documentation for localization in godot but have a few lingering questions I was hoping somebody might be able to help answer… I’m still early on in my messing with this particular project but localization seems like something that later you’ll be glad you put time and thought into early on

  • When using the gettext approach, how would things like resources have strings handled? For instance, if you have a resource file containing some info on an object or item (a name and description, for instance), there does not seem to be a way to add that resource to the list for automatic generation of a .pot file (.tres files in general can’t be selected for this I guess? which seems odd). I feel like I’m missing something there, or is it just expected that all the text lives in script files and UI elements and nowhere else…?

  • Alternatively with CSV files is there any remotely elegant way to handle multi-line strings? It seems very very unsuited for that role

  • I assume the gettext workflow completely breaks down if you try to use key entries instead of the original english text (and get the impression people would yell at you a lot if you did)

  • How acceptable is it considered to use something different to all of these in some (or even all) cases for the project? For instance, long descriptions like mentioned before being stored in separate files

gettext seems like the more robust approach but it also feels much more focused on projects that are effectively complete and having their translation done after-the-fact. And part of the desire for me would be making text handling easier for myself during development as well - being able to adjust strings in a more centralized way, though the .pot file’s comments calling back to the origin point of a string would help there.

So yeah I have kinda come away from the docs with more questions than answers… Maybe someone can help clear the air a bit for me. Feels worth mentioning this is just in a solo developer context too, not like some large project where it’d be sent off to a translation group, it’d be more me asking people I know directly to help with the strings etc

I find the Godot’s POT generation very unintuitive and cumbersome honestly. Thankfully, you don’t need to use it at all - just edit the POT and PO files directly with a text editor and add your translations there.
You can also use my plugin to convert CSV to POT/PO files.

It is indeed not suitable, but it’s not impossible. E.g. You can store line breaks as “\n”. E.g.

First line\nSecond line

Will print out

First line
Second line

But gettext is for sure better suited for this scenario.

It doesn’t break at all, it works perfectly fine with the keys. It’s officially not intended to be used this way, and indeed some purists will cut your head off, but technically nothing stops you from doing that. Some people even openly insist that it’s better this way. Whichever way you prefer - you should do it your way.

Not sure if I understand what you mean, could you elaborate? I don’t see a reason to store anything separately, all should be stored together.

That’s something I forgot to add to my post - doing it manually with the standard text-as-key approach seems rough, since you have to manually copypaste all the strings from the game in there and make sure it’s done so in a way that matches up correctly. Not so much an issue with tokens for keys though

It seems like a viable approach would be to use a key-based version internally and then have some post-process script down the road to take that and spit out a more “traditional” .pot file with text-to-text translations. That’s something I’ve been pondering as well and might be a good compromise.

Like, let’s say you have some RPG with big books of lore containing multiple pages of text, and you want that localized. Using gettext for that seems kinda…weird? At least with the text-to-text method again. Since it’d have to have the key be this massive string, and then the value be another massive string. Compared to just having something in plaintext as a resource and using the resource remapper for translating it instead.

I know I have a habit of worrying too much about the “proper” way of doing things admittedly.

Yeah, that’s one more reason to use the keys instead of actual text.
With CSV approach. You’d need to add the keys to your file manually anyway, so that’s more or less the same effort in both approaches.

If you use Poedit, and you have already the English translation done and saved as en.po, then when you open a new translation, e.g. pl.po, your keys will be automatically translated to the English text for you in the Poedit. Not sure if this is how all localization softwares work, but at least Poedit does it and it’s super convenient and accurately solves your problem here.

Yet another reason to use a key instead of text as a source. Length of the translated text doesn’t matter then, I don’t see any issue here. There’s no need for external files, that’d just complicate the setup.

Don’t we all?

Hmmm, that’s good to know - I do want to try and follow the standard workflow at least with regards to handing it off to others to help out rather than make them sift through what are probably likely to be keys incomprehensible to everyone but me :zipper_mouth_face:

Also, a followup question, thinking even further ahead. It would be nice for the game to be able to load translations at runtime for the ease of people working on them to test - as well as if people could offer up translations ready-to-go without needing to wait for the game to get updated (or to translate my project into pirate etc), but I can’t seem to find provision to load things like .po files at runtime

1 Like

Sounds good! :slight_smile:
You can send it over to me to translate into Polish, if you’d like, once you have it ready. I’d be happy to help. As long as it’s not a 200 pages novel you’re cooking there :slight_smile:

From messing with it a bit more, doing what I was thinking of doing is proving elusive… of course, what I’m trying to do is completely bass-ackwards from how the format is supposed to be used. I was trying to write a .po (not .pot) file by hand with ID tokens for msgid and then the english translation for msgstr, and then convert that into a .pot file, effectively swapping the msgstr and msgid values, to make a conventional format template file that fits better into the standard workflow. Wouldn’t be super hard to do with a custom script but I was seeing if there was some existing tool that could do it, especially due to cases like pluralization. And then from there re-convert it back into something that looks for the keys once a translated version exists.

I don’t remotely need this yet but well I have kinda been nerdsniped by this at this point so here we are…

My plugin allows you to easily convert CSV files into POT and PO files. Isn’t this what you’re trying to achieve?

It won’t take into account gettext-specific features like context awareness and pluralization, but that can be easily added afterwards for these couple of cases that actually require these features.

In any case - if you have suggestions on how to improve this addon, feel free to reach out, open an issue on GitHub, or even write the extension yourself and open a PR. We can make this workflow better.

Just wanted to quickly add to this point.
See below how it works in practice, Poedit asks me if I want to use text from en.po as a source.

And after I click “Load English”, I can see source text in English as well as the key next to it. Super convenient.

Sorry, I spent awhile working on other areas, starting to poke at this again.

I figured out how to get POEdit to do that, I just didn’t have the filenames/paths arranged the way it expected. So that should help.

I think I’ll stick with using PO-with-tokens and then later on work out a workflow for either generating a POT file to give to others to work on in the more conventional fashion or just see if they’re not going to be mad about dealing with tokens :face_with_tongue: Seems like the best compromise for me.