Share

Changes related to localization of MAXScript-exposed property names

3ds Max's support for language packs extends to names of scene element properties. These are displayed in the user interface, such as TrackView, and are also accessible to MAXScript and Python.

3ds Max allows for executing scripts that use English property names, using a different language pack. For example:

The scripts "b=Box length:20" and "b=Box longueur:20" would both work and produce the same results in 3ds Max running with the French language pack.

Modifying the length of the box instance in the Modify panel would result in an English script output to the Macro Recorder "$.length = 30".

The TrackView would display the Box's properties in French.

To achieve this, 3ds Max must maintain one mapping (dictionary) per supported language pack, between the English name of a property and its translation(s) in a certain supported language. Each of these dictionaries is kept in a file named maxscrpt.lcl in a corresponding language folder. For French, the mapping is in <3dsmax_install>\fr-FR\maxscrpt.lcl.

Note: To start 3ds Max with French language pack, run the following command: 3dsmax.exe -lang=FRA

Challenges with the current approach

In 3ds Max 2021 and older releases, this mapping could not be created for 3rd party plugins that choose to localize the names of the properties exposed to MAXScript, and so the capabilities described above would not work for these 3rd party plugins. 3rd party plugins would need to keep the name of their properties in English to allow scripts that use them to work when 3ds Max uses a non-English language pack.

The mapping described above has no sense of context. For example, an English property name could have multiple translations in a non-English language, or a non-English property name could have multiple English translations.

Solution implemented in 3ds Max 2022

The solution is to eliminate the need for the dictionaries (maxscrpt.lcl files) by allowing plugins to supply both the English and non-English names of their properties.

For 3rd party developers who provide localized versions of their plugins, this solution will allow their users to use their plugins in MAXScripts written either in English or one of the languages the plugin is localized for and 3ds Max also supports.

3rd party developers who do not provide localized versions of their plugins will not be affected by this solution, other than minor updates to their code.

The following changes implement the solution described above.

New virtual methods that were added

The following new method should return the same name that ClassDesc::ClassName() returns in locale en-US.

virtual const MCHAR* ClassDesc::NonLocalizedClassName() = 0; 

Non-Virtual methods whose signatures have been updated

The signatures of the following non-virtual methods have been updated:

MSTR Animatable::ClassName() 
MSTR ISubMap::GetSubTexmapTVName(int i) 
MSTR Mtl::GetSubMtlTVName(int i) 
ShapeObject::GetParameterName(int pbIndex) 

They now have a new parameter (bool localized) with a default value of true:

MSTR Animatable::ClassName(bool localized = true) 
MSTR ISubMap::GetSubTexmapTVName(int i, bool localized = true) 
MSTR Mtl::GetSubMtlTVName(int i, bool localized = true) 
MSTR ShapeObject::GetParameterName(int pbIndex, bool localized = true) 

If localized is true, then the name returned should be localized in the language 3ds Max is currently using. Otherwise, it should be the name in locale en-US.

The default value of true was added so that developers who do not localize their plugins only need to update the signatures of the methods listed above and not their call sites.

Developers who do localize their plugins should analyze the places where they call the methods listed above to decide what value to pass them for the new parameter (bool localized).

If, for example, a developer calls one of the methods listed above to get a name to display in the UI, they probably want to pass that method a value of true for the new parameter so that the string will be localized when it is displayed in the UI. On the other hand, if they are calling one of the methods above to perform a comparison with a hardcoded string that is not localized, then they must pass that method a value of false for the new parameter.

Virtual methods that were replaced

The following methods have been replaced and therefore sealed:

virtual void Animatable::GetClassName(MSTR& s) 
virtual Animatable* Animatable::SubAnimName(int i) 
virtual const MCHAR* BaseObject::GetObjectName() 
virtual MSTR Modifier::GetName() 
virtual MSTR SpecialFX::GetName() 
virtual MSTR BaseShader::GetTexChannelName(long nTextureChan) 
virtual MSTR ISubMap::GetSubTexmapSlotName(int i) 
virtual MSTR Mtl::GetSubMtlSlotName(int i) 
virtual MSTR MtlBase::GetFullName() 
virtual MSTR GizmoObject::GetParameterName(int pbIndex) 
virtual MSTR SimpleMod::GetParameterName(int pbIndex) 
virtual MSTR SimpleWSMMod::GetParameterName(int pbIndex) 
virtual MSTR SimpleObject::GetParameterName(int pbIndex) 
virtual MSTR SimpleWSMObject::GetParameterName(int pbIndex) 
virtual MSTR SimpleParticle::GetParameterName(int pbIndex) 
virtual MSTR SimpleShape::GetParameterName(int pbIndex) 
virtual MSTR SimpleSpline::GetParameterName(int pbIndex) 
virtual MSTR PBAccessor::GetLocalName (ReferenceMaker* owner, ParamID id, int tabIndex) 
virtual const MCHAR* CustAttrib::GetName() 

They have been replaced with the following methods, which have a new parameter in their signatures (bool localized):

virtual void Animatable::GetClassName(MSTR& s, bool localized) 
virtual Animatable* Animatable::SubAnimName(int i, bool localized) 
virtual const MCHAR* BaseObject::GetObjectName(bool localized) 
virtual MSTR Modifier::GetName(bool localized) 
virtual MSTR SpecialFX::GetName(bool localized) 
virtual MSTR BaseShader::GetTexChannelName(long nTextureChan, bool localized) 
virtual MSTR ISubMap::GetSubTexmapSlotName(int i, bool localized) 
virtual MSTR Mtl::GetSubMtlSlotName(int i, bool localized) 
virtual MSTR MtlBase::GetFullName(bool localized) 
virtual MSTR GizmoObject::GetParameterName(int pbIndex, bool localized) 
virtual MSTR SimpleMod::GetParameterName(int pbIndex, bool localized) 
virtual MSTR SimpleWSMMod::GetParameterName(int pbIndex, bool localized) 
virtual MSTR SimpleObject::GetParameterName(int pbIndex, bool localized) 
virtual MSTR SimpleWSMObject::GetParameterName(int pbIndex, bool localized) 
virtual MSTR SimpleParticle::GetParameterName(int pbIndex, bool localized) 
virtual MSTR SimpleShape::GetParameterName(int pbIndex, bool localized) 
virtual MSTR SimpleSpline::GetParameterName(int pbIndex, bool localized) 
virtual MSTR PBAccessor::GetLocalName (ReferenceMaker* owner, ParamID id, int tabIndex, bool localized) 
virtual const MCHAR* CustAttrib::GetName(bool localized) 

If localized is true, then the name returned should be localized in the language 3ds Max is currently using. Otherwise, it should be the name in locale en-US.

If a plugin does not provide localized string resources, it can disregard the localized parameter and always return the name in locale en-US.

You may be wondering why the methods in this section were sealed but not deprecated. This was done so that developers who do not localize their plugins only need to update the signatures of the methods listed above and not their call sites. To enable this, the default implementations of the sealed methods now call their new versions with localized set equal to true, which preserves the existing behavior.

Developers who do localize their plugins should analyze the places where they call the methods listed above to decide what value to pass them for the new parameter (bool localized). If, for example, a developer calls one of the methods listed above to get a name to display it in the UI, they probably want to pass that method a value of true for the new parameter so that the string will be localized when it is displayed in the UI. On the other hand, if they are calling one of the methods above to perform a comparison with a hardcoded string that is not localized, then they must pass that method a value of false for the new parameter.

Trackview and Controller property and sub-anim name support

Developers can now specify and retrieve nonlocalized names for their TrackView nodes and controllers using three new methods found in the MaxSDK::TranslationSupport namespace:

bool RegisterTranslation(ResolutionContext context, const MCHAR* localizedString, const MCHAR* nonLocalizedString) 
bool GetLocalizedStrings(ResolutionContext context, const MCHAR* nonLocalizedString, Tab<const MCHAR*>& localizedStrings) 
GetNonLocalizedStrings(ResolutionContext context, const MCHAR* localizedString, Tab<const MCHAR*>& nonLocalizedStrings) 

MAXScript scripts are typically written in English, but when MAXScript accesses names from TrackView nodes only localized names are available. By specifying localized and English string pairs using the RegisterTranslation() method, MAXScript can retrieve the English name corresponding to a localized name to see if it matches the property name or SubAnim name specified by a script.

Code that operates on TrackView nodes via ITrackViewNode::AddNode() or ITrackViewNode::AddController() should register via RegisterTranslation() the English name corresponding to the localized name specified in these methods.

TrackView nodes persist their names to the scene file, so to ensure that the translations are present when loading the scene file, the translations should be registered when the plugin dll is loaded. For an example of how to do this, see \maxsdk\include\TranslationSupport.h

New reference message added

This new message is sent by a parameter block to its client when it needs the nonlocalized animatable name of the i-th parameter.

REFMSG_GET_PARAM_NAME_NONLOCALIZED

If a plugin overrides ReferenceMaker::NotifyRefChanged(...), but does not provide localized string resources, it can simply handle this message in the exact same way that it handles REFMSG_GET_PARAM_NAME.

If a plugin overrides ReferenceMaker::NotifyRefChanged(...) and provides localized string resources, it can handle this message in the same way that it handles REFMSG_GET_PARAM_NAME, but the name returned should be in locale en-US.

New parameter definition tag added

The parameter definition tag below was added to the ParamTags enum:

p_nonLocalizedName

This new tag allows 3rd party developers to supply a non-localized parameter name for a parameter. The value assigned to this tag should be the parameter name in English, and it should be specified if the specified parameter name ("radialStrength" in the example below) is not the same as the localized name of the parameter acquired using the parameter's resource ID (IDS_EP_RDSTR in the example below).

PB_RADSTR, _T("radialStrength"), TYPE_FLOAT, P_ANIMATABLE + P_RESET_DEFAULT, IDS_EP_RDSTR, 
   p_default,          0.5f, 
   p_range,            0.0f,1000.0f, 
   p_ui,               TYPE_SPINNER, EDITTYPE_FLOAT, IDC_VORTEX_RD, IDC_VORTEX_RDSPIN, SPIN_AUTOSCALE,  
   p_nonLocalizedName, _T("Radial Pull Strength"),  
   p_end, 

As a result of the work that was done to remove the usage of the MAXScript.lcl files from 3ds Max, some MAXScript functions can now take the following optional parameters:

  • localizedName
  • localizedNames (note the plural form)

These parameters control whether localized or nonlocalized (English) property names are returned by functions they are passed to.

MAXScript functions that take the localizedName parameter

getObjectName <object> [localizedName:<bool>] 
getSubAnimName <obj> <index> [asString:<bool>] [localizedName:<bool>] 
getSubMtlSlotName <material> <index> [localizedName:<bool>] 
getSubTexmapSlotName (<material> | <texture>) <index> [localizedName:<bool>] 
getClassName <MAXWrapper> [localizedName:<bool>] 
WM3_MC_GetName [Morpher Modifier] [Channel Index] [localizedName:<bool>] 
WM3_MC_SetName [Morpher Modifier] [Channel Index] [Name String] [localizedName:<bool>] 

The localizedName parameter has a default value of true.

MAXScript functions that take the localizedNames parameter

getSubAnimNames <obj> [localizedNames:<bool>] 
dumpMAXStrings <file_name> which:[<array of maxclass>] PB2SpecialCases:[<bool>|<int>|<array of maxclass>] localizedNames:[<bool>] 

The localizedNames parameter has a default value of true.

The MAXModifier class now has a new read/write property: nonLocalizedName

Here is an example of how it can be accessed:

myMod = UVWMap()  

myMod.name -- Returns "UVW Map" 
myMod.nonLocalizedName -- Returns "UVW Map"  

myMod.name = "Bonjour" 
myMod.nonLocalizedName = "Hello" 

The MAXCustAttrib class now has a new read-only property: localizedName

Here is an example of how it can be accessed:

myCustAttrib = SimpleFaceData()  

myCustAttrib.name -- Returns "SimpleFaceData" 
myCustAttrib.localizedName -- Returns "SimpleFaceData" 

Translation support for Trackview nodes

Scripters can now specify and retrieve nonlocalized names for their TrackView nodes and controllers using the following new functions:

translationSupport.registerTranslation #trackViewNodes <localized string> <non-localized string> 
translationSupport.getLocalizedStrings #trackViewNodes <non-localized string> 
translationSupport.getNonLocalizedStrings #trackViewNodes <localized string> 

Was this information helpful?