Unfortunately, messages cannot be properly extracted from f-strings.
Use str.format() method instead.
Details
Diff Detail
- Repository
- rBA Blender Add-ons
Event Timeline
| object_print3d_utils/operators.py | ||
|---|---|---|
| 768–772 ↗ | (On Diff #54362) | This fix is unrelated to I18n, I can split it if needed. |
There was a discussion about formatting strings on devtalk, where %-formatting came out as preferred over str.format(). Not only does the %-formatting have my preference, it's also already used in the pose library code, so even for consistency I would recommend sticking to that.
| pose_library/operators.py | ||
|---|---|---|
| 34 | The newline here can be removed, the bpy imports can be grouped together. | |
| 374–379 | In the old code, the conditional expression was small, and only contained what needed to be conditional. The new code duplicates almost all the code there. I don't think this is an acceptable change, if only the formatting method is supposed to be altered here. | |
Hello, thanks for the review and thanks for the devtalk thread, I wasn’t aware of this.
I’ll make the requested change to printf formatting, but one strong argument in favor of the format() method, is that if the placeholders are named or numbered, word order can be changed. This is sometimes needed to get grammatical sentences in localization.
In this code there is only one placeholder per string though, so no problem.
| pose_library/operators.py | ||
|---|---|---|
| 374–379 | One possible intermediate solution is to do: self.report({"INFO"}, (tip_("Selected bones from %s") if self.select else
tip_("Deselected bones from %s")).format(pose_asset.name))Would that be all right? Otherwise, here is an explanation of why verbosity is strongly favored over this sort of constructions for internationalization. Admittedly, this particular instance will almost surely work in all indo-european languages, but what about others? What if a language’s grammar uses different word orders, like: “Bones selected from <name>”, but “In <name>, bones were deselected”? Furthermore, what if “Selected” and “Deselected” were already used elsewhere in the code base but in a different context? For instance, to mean “Selected [feminine plural]” or “the <thing> that <is / was / is to be> selected” or “the <thing> has now been selected”? There are 20 usages of “Selected” in Blender, and 6 usages of “Deselected”, and I not all of them have the same nuance. This results in poor, broken translations, a frustrating time for users and a very frustrating for translators and developers trying to disambiguate between various usages (that is, as far as I know, Bastien and I right now). My point is, we have no way to know for sure what is needed if we only consider the languages we already know about. A lesser issue is that it makes translators’ work harder, because they have to know the context of the sentence, but most translators are not developers, so it’s good if they don’t have to delve into the code to know what they’re doing. I’ll still revert if you insist, but using formatting to build sentences is very bad practice for i18n. | |
That's a good point. I wouldn't mind switching to that (either directly after this patch or when it actually becomes necessary), but then I feel it should be consistently done throughout the add-on, and not just for these few strings.
| pose_library/operators.py | ||
|---|---|---|
| 374–379 |
You make an excellent point, I'm glad to be corrected here :) I think it would still be nice to keep the construction of the message conditional, and take the call to self.report(...) out of the conditional, something like: if self.select: msg = tip_("Selected bones from %s") % pose_asset.name else: msg = tip_("Deselected bones from %s") % pose_asset.name self.report({"INFO"}, msg) | |
- Used the requested construct for the conditional
- Realized a few messages hadn’t been handled: I’d been focusing on f-strings, but printf formatting also has an issue. I’ll make a new pass in all official add-ons in a later patch to check what needs to be done.
I agree that consistency is good, at least per-add-on, and that a switch can be done later if the need arises.
I'd like to hear from @Bastien Montagne (mont29) on this: is pgettext_tip() the correct function for translating strings passed to operator.report({'LEVEL'}, message)? From the docs, it seems that pgettext_iface() would be a better choice here, but in other places I can see translated text for the report() function it's indeed using pgettext_tip().
As a confusing counter-example, bl_operators/wm.py seems to use pgettext_iface() for tooltips, and pgettext_tip() for reports.
That’s a fair question, and one that’s tripped me and basically everyone else a lot. I found from T76101#980282 about this comment by @Bastien Montagne (mont29) in rB23df1a774b5b:
Cheap tip: anything that is not "Camel Case" and/or that is more than a few words long should use TIP_ translation, not IFACE_ one.
This is not explicitly stated anywhere in the docs as far as I know, but that’s the current behaviour of report(). If you want to test that yourself, the following script prints the translated version of “Shader AOV” only if the Tooltips translation is enabled, and it uses report() in the operator.
import bpy
class SimpleOperator(bpy.types.Operator):
bl_idname = "object.simple_operator"
bl_label = "Simple Object Operator"
def execute(self, context):
self.report({"INFO"},"Shader AOV")
return {'FINISHED'}
def register():
bpy.utils.register_class(SimpleOperator)
if __name__ == "__main__":
register()
bpy.ops.object.simple_operator()By the way, I learned that Python’s printf-style formatting actually does provide a way to use named placeholders:
print('%(language)s has %(number)03d quote types.' %
{'language': "Python", "number": 2})I’ve never seen it used and think it looks a bit weird, but with it there is no need to switch to format() for i18n.
Re tip vs iface, main point has already been said, will update the official docs asap with detailed reasoning.
Re format, please do not use any fancy feature like named or positional parameter for now, this would require rather complex updates in i18n scripts. We have to ensure translated messages do not butcher the expected format parts (which happens very often already with the basic ones we currently support, and can easily lead Blender to crash when in C strings).
If we want to go that way, we first need to invest some dev time in our i18n tooling.
Duly noted, thanks! I wasn’t planning to do that any time soon, just noting that it is possible. If the need ever arises, I’ll make sure to consult and test before using such features. :)
I messed up the merging from the v3.4 branch to master and instead cherry-picked, but it will merge fine if someone makes a new commit and merges again to master.