How can I add a menu to .res file for muti-language support?
-
Hello!
I try to use a
DIALOG.res
file to support muti-language plugins, but I didn't find a way to add menus or menu button for that. Is it possibale to support another language for menu withLoadDialogResource
?Now I can just add Menu in original python functions like this:
def CreateLayout(self): # custom menu add sm = SubMenu(["A", "B", -1"], self.ID_MENU) sm.add_menu(self) if self.GroupBeginInMenuLine(): self.AddButton(self.ID_MODELFOLDER, c4d.BFH_LEFT | c4d.BFV_TOP, initw=0, inith=0, name='Button1') self.GroupEnd() return self.LoadDialogResource(MY_DIALOG)
Thanks.
-
Hey @dunhou,
Thank you for reaching out to us. You can load localized strings with c4d.plugins.GeLoadString(). So, when you had this project structure:
. └── MyPlugin/ ├── res/ │ ├── dialogs/ │ │ └── ... <- The dialog gadget resources, i.e., *.res files │ ├── strings_en-US/ │ │ ├── dialogs/ │ │ │ └── ... <- Contains the English dialog gadget string definitions │ │ └── c4d_strings.str <- Contains the English string definition for IDS_MY_STRING │ ├── strings_zh-CN/ │ │ ├── dialogs/ │ │ │ └── ... <- Contains the Chinese dialog gadget string definitions │ │ └── c4d_strings.str <- Contains the Chinese string definition for IDS_MY_STRING │ └── c4d_symbols.h <- Contains all symbol definitions, including IDS_MY_STRING └── myPlugin.pyp
Then you can load the localized string for the ID
IDS_MY_STRING
as follows:myLocalizedString: str = c4d.plugins.GeLoadString(IDS_MY_STRING)
When the user is running his/her Cinema 4D in English, it will load the string from
/strings_en-US/c4d_strings.str
. When the user is using a Chinese Cinema 4D instance, it will load from/strings_zh-CN/c4d_strings.str
. For users who run any other language instance, it will also useen-US
as that plugin does not provide any other localized strings, and Cinema 4D will default in that case toen-US
.Just as for
LoadDialogResource
, the plugin resource must be exposed as__res__
in the global scope of the module (which will happen automatically in a*.pyp
file). If you are in a module where this is not the case, you must either init the resource there yourself in such way, or right away usec4d.plugins.GeResource.LoadString
.Cheers,
Ferdinand -
Thanks for the detailed explain.
It seems I should add the menu and menu botton in the
.res
file for use this.str
file.Now the main GUI already worked , but I didn't find a way to add menu or button in
.res
file. or reg a unvisibale text indialog.res
.Did I understand something wrong , could you privide a mini code for this?
My didn't work code:
**IDS_MY_STRING** : { IDS_MENU "My Test"; } **c4d_symbols.h** : enum { _FIRST_ELEMENT_ = 20000, IDS_MENU, _DUMMY_ELEMENT_ }; **plugin.pyp** : myLocalizedString: str = c4d.plugins.GeResource.LoadString(IDS_MENU)
-
Hello @dunhou,
this defintion seems mostly correct, the file IDS_MY_STRING should however be named c4d_strings.str. You put there the general prupose string defintions for a language (e.g., strings for menus).
In Python you must also manually expose the symbols for your string as Python is not importing automatically them automatically as C++ does. So, when your symbols defintions is
c4d_symbols.h
enum { //_FIRST_ELEMENT_ = 20000 // Not really required IDS_MENU, _DUMMY_ELEMENT_ };
then you must either define in your Python file somewhere that
IDS_MENU: int = 20001
or use the raw integer value in calls instead.Cheers,
Ferdinand -
I try to do this but it report a bug , here is a more part of codes:
plugin.pyp
ID_MB = 100 # button ID def CreateLayout(self): IDS_MENU: int = 20002 # MENU str ID in c4d_symbols.h myLocalizedString: str = c4d.plugins.GeResource.LoadString(IDS_MENU) # buttton in menu if self.GroupBeginInMenuLine(): self.AddButton(self.ID_MB, c4d.BFH_LEFT | c4d.BFV_TOP, name=myLocalizedString) self.GroupEnd() return self.LoadDialogResource(20001) # DIALOG ID in c4d_symbols.h
c4d_symbols.h
enum { _FIRST_ELEMENT_ = 20000, IDD_DIALOG, IDS_MENU, _DUMMY_ELEMENT_ };
IDD_DIALOG.res in
resources
:DIALOG IDD_DIALOG { NAME IDS_DIALOG; SCALE_V; SCALE_H; }
c4d_strings.str in
strings_en-US/dialogs
folder{ IDS_DIALOG "Title"; IDS_MENU "button1"; }
console reports
myLocalizedString: str = c4d.plugins.GeResource.LoadString(IDS_MENU) TypeError: descriptor 'LoadString' for 'c4d.plugins.GeResource' objects doesn't apply to a 'int' object
Thanks
-
sorry for my bad internet those days,It will duplicate post sometimes
-
Hello,
eh, my bad I overlooked that you were using an incorrect call for loading the string. Since you you are in the pyp file, you do not have to init the ressource yourself, as the module attribute
__res__
of that file already contains it (just put aprint(__res__)
anywhere in your code in the pyp file, and you will see that it is aGeResource
instance).In your case you just have to use
myLocalizedString: str = c4d.plugins.GeLoadString(IDS_MENU)
which will then use the resource
__res__
which has been build automatically by Cinema 4D for your plugin. It is only when you are outside of that pyp file, that you must build theGeResource
When you are in such scenario or just want to generally load a custom resource, it will work something like this:
resource: c4d.plugins.GeResource = c4d.plugins.GeResource() # resourcePath is the root folder of where the resource is located. E.g., you have the plugin MyPlugin # at the directory /MyPlugin with a directory /MyPlugin/res in it, then resourcePath would be /MyPlugin resource.Init(resourcePath) # You can then either expose this resource as the module resource and then use c4d.plugins.GeLoadString # as in a pyp file. global __res__ __res__: c4d.plugins.GeResource = resource myLocalizedString: str = c4d.plugins.GeLoadString(IDS_MENU) # Or use the resource instance directly. myLocalizedString: str = resource.GeLoadString(IDS_MENU)
Cheers,
Ferdinand -
Sorry to reply late. Little busy before.
I am a little confused , in python document
Plugin Structure
, This page said plugin structure like this:
It's different with the structure above.And another problem is : In my mind ,
string_us/dialogs/IDD_DIALOG.str
, thisstr
file define the dialogs texts string , but thestring_us/c4d_strings.str
file , It's a little confused , is it define the strings in the menu of the dialog? If it is , how can I set the ID of this string ?And in the code above,
myLocalizedString: str = c4d.plugins.GeResource.LoadString(20002)
, this line cann't find a string inIDD_DIALOG.str
with the ID(int) inc4d_symbols.h
.
-
Hello @dunhou,
You are right, my little diagram was not correct, the string folders must be part of the resource, I forgot to indent them one level. I have fixed the diagram.
The string definitions in
language\dialog
, e.g.,string_us/dialogs/myDialog.str
, define the strings referenced by a dialog definition, e.g.,dialogs/myDialog.res
. E.g., when you have some dialog gadget which is defined to haveNAME IDS_MY_GADGET
inmyDialog.res
, themyDialog.str
files for all languages must define a stringIDS_MY_GADGET
.The file
c4d_strings.str
on the other hand is used to store generic string, and it is not specifically made for menus, it is just one way you can use it. There is no (public) mechanism to define menus in resources, so, all you can do is to define strings in thec4d_strings.str
files and then load strings for the currently active language inside Cinema 4D to build a localized menu out of them.You can see this multipurpose nature at the example of the strings_en-US/c4d_strings.str file of the Asset API Examples Plugin. I stored there all kinds of string information, ranging from tooltips, over the names of the examples and their short descriptions, to some URLs.
c4d_strings.str
does exactly what its name implies, it stores generic strings.I hope this clarifies things.
Cheers,
Ferdinand -
A lot of appreciate!
It works very well , I found when I have a
c4d_strings.str
, themyDialog.str
must have SAME name asmyDialog.res
. If it doesn't have ac4d_strings.str
file and work with only one dialog , themyDialog.str
can have another name and it works well too.(I am not sure this qustion should be post there or in a new blog )
And a little thing I am not sure is : those ID formc4d_symbols.h
contain the ID of text/bitmap/button/... , in this case main dialog ID is 20000 , but if I have mutiple dialogs or sub dialogs from the main dialog(even another plugins), what is happend if those dialog's ID contain some same numbers? -
Hey,
but if I have mutiple dialogs or sub dialogs from the main dialog(even another plugins), what is happend if those dialog's ID contain some same numbers?
You cannot do that, all integer identifiers for dialogs within a project (i.e., stuff the is located in the same resource directory) must be unique. Except for cycle values, they only do have to be unique within the scope of the cycle/dropdown they are being defined for.
If you really want to have two dialogs which share ID values for gadgets, then you must define them in different projects, each with their own resource directory. Which will also mean that the two dialogs cannot access each other's implementation (in an easy manner, you could of course cook something up with messages for example).
Cheers,
Ferdinand -
Is that means if I have a
plugin A
with unique IDA
, the dialog ID100
likesA-100
inside , andplugin B
with unique IDB
, the dialog ID100
likesB-100
, so that they can exist at same time ?And those
A-100
like IDs will dispear when close c4d , so I don't have to unreg them :eg.: bitmapbutton IDs manually? ( c4d.gui.RegisterIcon / c4d.gui.UnregisterIcon) -
Is that means if I have a plugin A with unique ID A , the dialog ID 100 likes A-100 inside , and plugin B with unique ID B , the dialog ID 100 likes B-100 , so that they can exist at same time ?
Yes, other than plugin IDs, resource IDs are scoped within the resource they are being used for.
plugin A
is simply not aware of the resources ofplugin B
.And those A-100 like IDs will dispear when close c4d , so I don't have to unreg them :eg.: bitmapbutton IDs manually? ( c4d.gui.RegisterIcon / c4d.gui.UnregisterIcon)
You do not have to register or unregister anything in this context.
Cheers,
Ferdinand -
Thaks for all of that explains .
With your detailed and friendly explains , A new habbitor like me can code within Cinema 4D as well . Thanks for that !