SOLVED How to use STRINGTABLE with a Python Command Plugin

Hello,
I'm trying to add multi-language support in my plugin.

I currently have the strings hardcoded in the GeDialog's CreateLayout method like this:

self.AddStaticText(self.HELP_ID, c4d.BFV_BOTTOM | c4d.BFV_SCALE | c4d.BFH_LEFT, 0, 0, "Help")

All of the strings are labels for UI menu commands and gadgets. I understand that the best practice is to put the UI strings into folders within a 'res' folder like so:

res
└ strings_en-US
    └ c4d_strings.str
└ strings_fr-FR
    └ c4d_strings.str

My issue is I don't know how to use the STRINGTABLE in the .str with my Python CommandPlugin. Here's an example of the c4d_strings.str content:

c4d_strings.str (strings_en-US)

STRINGTABLE
{
	IDS_TEST "Test Plugin";
	TITLEVIEW_ID "Test - View";
	
	HELP_ID "Help";}
}

c4d_strings.str (strings_fr-FR)

STRINGTABLE
{
	IDS_TEST "Test Plugin";
	TITLEVIEW_ID "Test - Vue";
	
	HELP_ID "Aide";
}

Please forgive all of these questions:

  • What is the best practice for loading these STRINGTABLES and using them in the UI? I couldn't find an example using a CommandPlugin in the official Cinema 4D Python examples though I did see that the py-ies_meta_r12.py SceneSaverPlugin example uses a method called c4d.plugins.GeLoadString.
  • If GeLoadString is the way to go, would I set up my IDs in my GeDialog class and reference the corresponding ID in the STRINGTABLE with this method?
  • Would Cinema 4D then grab the correct string based on the user's language if my folder structure is set up appropriately?
  • Is there a similar method for automatically handling the localized date and time format?
  • As mine is not an Object plugin, I won't need a description folder, is that correct?
  • What should I do with the c4d_symbols.h file?

Thank you so much!

Hi, @blastframe as @PluginStudent pointed you should use GeLoadString.

To make GeLoadString working, you should use as you already did, the .str file but also the c4d_symbols.h so this way Cinema 4D can link the ID you provided in the str file to an actual int value.

res
└ c4d_symbols.h
└ strings_en-US
    └ c4d_strings.str
└ strings_fr-FR
    └ c4d_strings.str

c4d_symbols.h

enum
{
	IDS_TEST = 10000,
	TITLEVIEW_ID,  // This will have id 10000 + 1
	HELP_ID, // This will have id 10000 + 2
	
	// End of symbol definition
	_DUMMY_ELEMENT_
};

However, due to the nature of Python, the int value defined in the c4d_symbols.h is not ported to python so you have to redefine it in your python code as well.

And here is how to use it in your pyp file. This needs the __res__ to be defined, this is done automatically, for more information see Issue with registering a ToolData.

print c4d.plugins.GeLoadString(10000) # Will print the str value of IDS_TEST 

Finally, note that these Symbol IDs must be unique through all Cinema 4D so instead of IDS_TEST is would recommend you rename it with BLASTFRAME_IDS_TEST .

Cheers,
Maxime.

GeLoadString() is the way to go. You actually find plenty of examples of GeLoadString() being used in the C++ code base (e.g. asynctest.cpp) and this doc: String Manual (Classic).

Hi, @blastframe as @PluginStudent pointed you should use GeLoadString.

To make GeLoadString working, you should use as you already did, the .str file but also the c4d_symbols.h so this way Cinema 4D can link the ID you provided in the str file to an actual int value.

res
└ c4d_symbols.h
└ strings_en-US
    └ c4d_strings.str
└ strings_fr-FR
    └ c4d_strings.str

c4d_symbols.h

enum
{
	IDS_TEST = 10000,
	TITLEVIEW_ID,  // This will have id 10000 + 1
	HELP_ID, // This will have id 10000 + 2
	
	// End of symbol definition
	_DUMMY_ELEMENT_
};

However, due to the nature of Python, the int value defined in the c4d_symbols.h is not ported to python so you have to redefine it in your python code as well.

And here is how to use it in your pyp file. This needs the __res__ to be defined, this is done automatically, for more information see Issue with registering a ToolData.

print c4d.plugins.GeLoadString(10000) # Will print the str value of IDS_TEST 

Finally, note that these Symbol IDs must be unique through all Cinema 4D so instead of IDS_TEST is would recommend you rename it with BLASTFRAME_IDS_TEST .

Cheers,
Maxime.

Since this topic is older than a week I marked is as closed, but feel free to reopen it if you have further questions.

Cheers,
Maxime

@m_adam Hi Maxime! I wanted to make sure I had all of my strings before implementing this. I am very grateful for your help. I will come back to this thread soon! Thank you!

@m_adam I got it working with your help! Thank you so much. I have a couple more questions (more for advice really):

  1. Are there tools for creating C++ STRINGTABLES like the ones Cinema 4D uses?
  2. I am currently using a spreadsheet for editing my stringtables. Are there any tools I can use to export the non-ascii characters as unicode that Cinema 4D will understand?
    Thank you!

I don't think there are any official "tools". But the docs describe how to handle unicode (Dialog Layout😞

The encoding is 7-bit ASCII with other characters encoded as \uHHHH. For example 'Natürlich' is written 'Nat\u00fcrlich'. Byte-order marks are not used.