-
Notifications
You must be signed in to change notification settings - Fork 21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Translation infrastructure #295
Conversation
Note that if you want to set the tool locale, you have to use |
Turns out you can do this in 4.4 using the new TranslationDomain support. I added a couple commits to do that and it seems to work. Still needs cleanup, though. I'm going to reopen and mark as a draft. |
23afc7d
to
8163278
Compare
This is really ready for review now. I tested with 4.3 and 4.4-dev3. The editor translations only show up in 4.4, but I added some bogus translations and they do work there. |
Since BlockDefinitions are generic Resources, Godot doesn't automatically extract translatable strings from them. In order to do that, we need to provide an EditorTranslationParserPlugin that extracts the desired fields so they're included in a POT file.
These add utilities for using translations with Godot 4.4's TranslationDomain support. Besides helpers to access the translation domain, there are also utilities to load the translations from PO files. Much of the code is there to run on pre-4.4 Godot without causing errors.
This will add the godot_block_coding translation domain and include translations from any PO files in the locale directory. This is done during plugin initialization so that the translations are loaded before any component tries to translate a string.
Nodes inherit the translation domain of their parent by default, so setting our translation domain on a few top level nodes will make all of our nodes translated. The domain is set in _init() to ensure that child nodes inherit the domain before they start translating strings.
Since the inspector plugin isn't a child of our UI, it needs to have the translation domain set explicitly.
8163278
to
f805d5b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very nice! Obviously I'm offended that there is no placeholder en_GB.po
file.
push_warning('Unrecognized game result "%s"' % result) | ||
|
||
|
||
func reset(): | ||
text = "" | ||
# Workaround for POT generation extracting empty string. | ||
text = str("") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Weird, so it extracts constant strings for translation even if not tagged with tr()
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's actually a little uglier than that now that I've read through the GDScript translation parser. The assumption is that anything that assigns to a text
property is a Label
or similar, which should be automatically translated. In this case, it is a subclass of a Label
, so that's a correct assumption. However, I don't think it should try to translate an empty string.
I wouldn't have cared except that msgmerge
errored on a duplicate empty string msgid.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Weird. I can see the appeal of not having to litter the code base with tr()
at some level, but it seems error-prone.
@@ -56,6 +56,7 @@ func _enter_tree(): | |||
|
|||
# Custom Project->Tools menu items. | |||
add_tool_menu_item(tr("Regenerate %s POT file") % "BlockCode", TxUtils.regenerate_pot_file) | |||
add_tool_menu_item(tr("Update %s translated files") % "BlockCode", TxUtils.update_pot_files) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder how feasible it would be to have a CI job that runs a (real, not headless) editor and then executes these...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good question. The TxUtils.update_pot_files
one is independent of the editor, so you could write a headless SceneTree
script that we could run from CI. I almost started doing it but decided to wait for now since TxUtils.regenerate_pot_file
is very much dependent on the editor and I had no idea if that could be scripted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One for another day I think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I figured this out (at least one way). It's kinda nasty, but I'll make a PR for it.
We want the strings in the BlockDefinition to stay untranslated and only translate them when being displayed in the UI.
Neither the user's entered variable name nor the type string should be translated, so set auto_translate_mode to disabled.
These represent block argument values and shouldn't be translated.
Workaround an issue with POT generation where the empty string is extracted for translation. Using str() avoids POT generation since the string isn't evaluated at compile time. See godotengine/godot-proposals#10590.
Currently the only way to regenerate a POT file is to go through the Regenerate POT dialog in the Localization tab of the Project Settings. This adds a Project->Tools menu item that drives the dialog from code using our POT file path. Hat tip to KoBeWi for the procedure. Hopefully in the future we'll have a Godot CLI interface to do this.
The list of translated files to include in the POT file is stored in the project settings. The primary way to update it is through the POT Generation dialog when a new file is added, but we know that we generally want to add all of the plugin's files for translation. This adds a Project->Tools menu item that finds all the relevant files and updates the project setting.
This is the result of running TxUtils.update_pot_files() and TxUtils.regenerate_pot_file() via the Project->Tools menu. There are definitely some strings extracted from scene files that we don't want translated, but I'm not sure the best way to do that without completely taking over scene file parsing. Just ignore the issue for now...
Create message catalogs for all the locales supported in the Godot editor except for es_AR and pt_BR. Due to a bug in Godot's locale matching[1], these would be preferred over the country-less variants for all countries. For example, the pt_BR translation would be used for pt_PT when the generic pt translation would be preferred. The catalogs were created like so: ``` msginit --no-translator -l $locale -i godot_block_coding.pot -o $locale.po ``` The catalogs are also added to the project settings despite that setting not being used in the editor. Nodes that are used in games such as the simple nodes can still get translations via the main domain this way. 1. godotengine/godot#90677
A bit of info about working with the translations.
f805d5b
to
6d149f0
Compare
This is a WIP branch I had to provide translations for the plugin. It would need some work regardless, but it turns out that editor translation plugins don't work, so it's a moot point. Just putting it here for reference and in the unlikely scenario that Godot does start supporting editor plugin translations.
This has now been updated to use TranslationDomain, which was added in Godot 4.4-dev3. If you run 4.4-dev3 and put some translations in the PO files, you'll see them in the editor.
Fixes: #146
Fixes: #152
https://phabricator.endlessm.com/T35705
Here's a tool script I was using to test that ultimately proved to me that translations weren't happening with
TranslationServer
: