Translation of plural forms with CSV

Godot Version

v4.4.1.stable.mono.official [49a5bc7b6]

Question

I’m trying to figure out how to translate strings that can have different form depending on context. For example let’s assume you have key APPLE with english string apple, and also translated to other languages as needed. And then in code:

body.Text = Tr($"{x} APPLE");

And this thing won’t work properly, because for example it will display “5 apple”, which is not correct.

Apparently this issue can be solved by Tr_n() method, but there are no usage examples in documentation, and from what I’ve seen on github it doesn’t actually support csv format, and people have been begging for csv support for over half a decade, and there is no support to be seen.

And this makes me confused. Because from what I’ve seen, 99% of tutorials show how to handle translation with CSV files. And if CSV files do not support different contexts, this means CSV translations are literally useless outside of extremely tiny and simple games.

Is there any way to bypass this issue for CSV? Because otherwise I will be forced to use PO files. I tried at first, but after 3 hours of digging and trying multiple PO edit software, I couldn’t even figure out how to create PO file, this is some hidden and secret knowledge that no one wants to share. CSV is very convenient because you have one spreadsheet where you can easily access every key.

Unfortunately, no. The only solution is using the gettext and POs.
But it’s not that hard to set up actually. I agree there’s not much good documentation for it, as mostly tutorials and docs focus on CSV translations.

Let me know what issues you had when setting up the gettext solution and I can try to help you resolve it.

1 Like

I have already found quite decent tutorial, it doesn’t cover everything but I should figure it out. But there is one issue I’ve encountered - Godot POT generator doesn’t know that C# exists, so I have to manually insert some keys that are used exclusively in scripts. I have only found out that you should not do that under any circumstances, but sometimes you have to do stupid things to bypass limitations.

I also could create hidden labels so POT generator can catch them, but that sounds silly to me.

The documentation has a section about localization using gettext

Here’s a video tutorial about generating the POT and PO files https://www.youtube.com/watch?v=lwQs-h7kyuM

For C# you’ll need to write a plugin to support it. You could use this one as a base GitHub - Renari/Godot-CSharp-Translation-Parser: A Godot editor plugin that will look for calls to the Godot TranslateServer Translate method and add them to generated POT files.

2 Likes

I wanted to create a “database” for script keys in dummy gd file, but plugin should work fine too. I just need to figure out how to install it, but I should handle it.

Edit

Something doesn’t work in here:
Unable to load addon script from path: 'res://addons/Godot-CSharp-Translation-Parser/CSharpParserPlugin.cs'. This might be due to a code error in that script. Disabling the addon at 'res://addons/Godot-CSharp-Translation-Parser/plugin.cfg' to prevent further errors.

I will consider the dummy gd file as a solution for the time being.

alternatively you could do something like:

today we give out {NUMBER} <apple|apples>

and then handle the formatting in a method somewhere that chooses the correct word based on the number

Unless you’re dealing with a fun language like Polish, where you have 1 singular and 2 plural forms:

  1. numbers ending with 2, 3, 4 (except 12, 13, 14)
  2. all other numbers

And there are most likely other (and probably even worse) examples in other languages.
So your translation algorithm will quickly become a mess :slight_smile:

While gettext already accounts for all that.

2 Likes

I have set up dummy gd script that has nothing but tr and tr_n in _ready and it works just fine, Poedit seem to handle all kinds of plural forms (I’ve tested on only two languages, I’m not fluent in more).

The only issue is that instead of POT generator looking into .cs scripts for me, I have to manually manage keys that are used in .cs scripts.

I could try making my own addon, but I don’t like regex, and I have no idea how to write Godot addons.

1 Like

We’ve all been there :smiley:
I have a similar thing in one of my projects and probably will have to reuse this idea soon enough.

Since some dev build of Godot 4.6 it is possible to use plurals witch CSV.

Example:

This is my CSV:

en,?plural,es
PluralTranslate,,
There is %s apple,There are %s apples,Hay %s manzana
,,Hay %s manzanas
Godot is cool,,Godot es genial
PluralTranslate,,
You got %s EXP point,You got EXP %s points,Obtuviste %s punto de EXP
,,Obtuviste %s puntos de EXP

The structure, from what I can understand, is something like this:

PluralTranslate,, # only if the next message is pluralizable
# Next line: the default language singular form, the default language plural form,other languages singular form
There is %s apple,There are %s apples,Hay %s manzana
# Next line: plural forms for all languages except the default one.
,,Hay %s manzanas

And here is how to use it:

extends Node2D


func _ready() -> void:
	var locales: Array[String] = ["en"]
	for locale in TranslationServer.get_loaded_locales():
		locales.append(locale)
	
	for locale in locales:
		TranslationServer.set_locale(locale)
		print(TranslationServer.get_locale_name(locale))
		print("-----------------------")
		for n in 2:
			var count: int = n + 1 
			print(tr_n("There is %s apple", "There are %s apples", count) % count)
		print("=======================\n\n")
	
	for locale in locales:
		TranslationServer.set_locale(locale)
		print(TranslationServer.get_locale_name(locale))
		print("-----------------------")
		for n in 2:
			var count: int = n + 1 
			print(tr_n("You got %s EXP point", "You got %s EXP points", count) % count)
		print("=======================\n\n")
	
	print("********\n\n")
	
	for locale in locales:
		TranslationServer.set_locale(locale)
		print(TranslationServer.get_locale_name(locale))
		print("-----------------------")
		print(tr("Godot is cool"))
		print("=======================\n\n")

When you run this, the expected output is:

English
-----------------------
There is 1 apple
There are 2 apples
=======================


Spanish
-----------------------
Hay 1 manzana
Hay 2 manzanas
=======================


********


English
-----------------------
You got 1 EXP point
You got 2 EXP points
=======================


Spanish
-----------------------
Obtuviste 1 punto de EXP
Obtuviste 2 puntos de EXP
=======================


********


English
-----------------------
Godot is cool
=======================


Spanish
-----------------------
Godot es genial
=======================

There is also support for more complex pluralization rules. For an example of that, check the test project on this PR: Improve CSV translations by timothyqiu · Pull Request #112073 · godotengine/godot · GitHub

2 Likes