Skip to content

Translation export generates msgctxt / plural entries that Godot runtime cannot resolve #39

@elasrlambert

Description

@elasrlambert

Description

Hi, first of all thank you for the Quest System plugin — it’s very helpful.

While using the built-in translation export with Godot 4.5, I ran into an issue where quest-related translations are correctly generated into .po files, but cannot be resolved at runtime using tr(), even though the keys match exactly.

After debugging, I found that this is caused by how EditorTranslationParserPlugin is currently used in the plugin.

Root Cause Analysis

In EditorTranslationParserPlugin._parse_file(), the plugin returns a PackedStringArray with three elements per property:

msgids.append("quest_%s/%s" % [res.id, property.name])
msgids.append("Quest ID: %s, property: %s" % [res.id, property.name])
msgids.append("quest_%s/%s_plural" % [res.id, property.name])

However, in Godot’s translation pipeline:

  • PackedStringArray[0] → msgid
  • PackedStringArray[1] → msgctxt
  • PackedStringArray[2] → msgid_plural

This causes Godot to automatically generate .po entries like:

msgctxt "Quest ID: 1, property: quest_description"
msgid "quest_1/quest_description"
msgid_plural "quest_1/quest_description_plural"
msgstr[0] ""

Problem

Godot runtime (TranslationServer) does not support:

  • msgctxt
  • msgid_plural
  • msgstr[n]

As a result, any entry generated this way is silently ignored at runtime, and tr("quest_1/quest_description") simply returns the key.

My fixed version:
translation_plugin.gd

## plugin version:
#func Wrong_parse_file(path: String) -> Array[PackedStringArray]:
	#var res := ResourceLoader.load(path)
	#if not res: return []
	#if not res is Quest: return []
#
	#var ret: Array[PackedStringArray] = []
	#for property in res.get_script().get_script_property_list():
		#var msgids: PackedStringArray = []
		#if property.type != 4: continue # If the property is not a string, we skip it
#
		## Here we check if the property is an exported variable
		#if property.usage == PROPERTY_USAGE_SCRIPT_VARIABLE | PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR:
			#msgids.append("quest_%s/%s"% [res.id, property.name]) # quest_1/quest_name
			#msgids.append("Quest ID: %s, property: %s"% [res.id, property.name]) # quest_1/quest_objective
			#msgids.append("quest_%s/%s_plural"% [res.id, property.name]) # quest_1/quest_description
#
			#ret.append(msgids)
#
	#return ret
	
## My version:
func _parse_file(path: String) -> Array[PackedStringArray]:
	var res := ResourceLoader.load(path)
	if not res or not res is Quest:
		return []

	var ret: Array[PackedStringArray] = []

	for property in res.get_script().get_script_property_list():
		if property.type != TYPE_STRING:
			continue

		if property.usage == PROPERTY_USAGE_SCRIPT_VARIABLE | PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR:
			var msgids := PackedStringArray()
			msgids.append("quest_%s/%s" % [res.id, property.name])
			ret.append(msgids)

	return ret

Information

  • Version of Godot affected: 4.5.1.stable
  • Version of QuestSystem: 2.0.1

Image
Image

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions