Internationalization Process
Internationalization enables the plug-in to support localized strings in its user interface and to operate correctly in localized environments. The process of localizing the string resources is a separate set of steps outlined later in this document. Localization can take place any time after the plug-in has been internationalized, the plug-in will continue to work using the default resource values until translations are available.
The following steps are involved in internationalization:
- Determine what aspects of the plug-in will require localization.
- Identify the user interface strings that need to change when the plug-in is running in another language. The strings may be located in C++ code or MEL scripts. Modify the code and scripts to use string resources in place of hard-coded strings.
- Add string registration calls to the plug-in initialization sequence.
- Review string handling code and make modifications as required to properly handle strings in both single and multi-byte encodings.
Assessing Requirements
Before doing the work to internationalize a plug-in, it is useful to assess what the actual requirements are. Not all aspects of plug-in internationalization may be necessary or appropriate for each situation.
The most important consideration is to determine the number of target languages the plug-in needs to support. If a plug-in is only targeted at a single language (for example, only Japanese) it may be possible to supply the custom UI for the plug-in in a localized script instead of using string resources.
String resources are required if the plug-in meets one or more of the following criteria:
- the plug-in is to support more than one user interface language (for example, English and Japanese)
- the plug-in issues user messages from C++ code (for example, calls to
displayError()
) - the plug-in creates nodes which are to be displayed in the UI with localized node and attribute name labels
Another consideration is the type of string handling and I/O the plug-in code performs. Plug-ins with little or no string manipulation or file handling should require few changes, while others will need to be reviewed to ensure they are dealing properly with multi-byte characters.
Defining and Registering String Resources
This section will discuss how string resources are used in C++ and MEL scripts, and how to register them during plug-in initialization.
String Resource Keys
Each string resource in the Maya String Catalog is identified by a unique key. For plug-ins, the key consists of an ordered pair of strings. The first element of the pair is the plug-in name, which will be the same for all strings used by the plug-in. The second part of the key is a unique identifier for the string being defined. For example, string resource keys used by the closestPointOnCurve
plug-in will have the form:
("closestPointOnCurve", "stringId1");
("closestPointOnCurve", "stringId2");
("closestPointOnCurve", "stringId3");
String Resources in C++
The MStringResourceId
and MStringResource
classes are used to define and access string resources in the plug-in C++ code. The MStringResourceId
constructor accepts three arguments, the two elements used to form the resource key, and the default value of the resource string. In the example below the plug-in name is "closestPointOnCurve
", and the unique user-defined key being given to this string is "kNoValidObject
".
MStringResourceId invalidObject("closestPointOnCurve", "kNoValidObject", "A curve or its transform node must be specified as a command argument, or using your current selection.");
Using #define statements to associate the MStringResourceId
with a constant is helpful to provide a single point of definition for each resource. Typically the MStringResourceId
declarations for the plug-in will be grouped together in a header file that is shared between the C++ modules that require it.
#define kPluginId "closestPointOnCurve"
#define kNoValidObject MStringResourceId(kPluginId, "kNoValidObject", \"A curve or its transform node must be specified as a command argument, or using your current selection.")
The MStringResource::getString
method is used to look up the catalog entry when the string is needed in the code. The catalog lookup will return either the default value or localized string value (if it is available). Once the string is loaded it can be used like any other MString
.
MStatus stat;
MString msg = MStringResource::getString(kNoValidObject, stat);
displayError(msg);
A string resource cannot be accessed until it has been registered. This is done by calling MstringResource::registerString
. The registration steps are described below under String Resource Registration.
String Resources in MEL Scripts
MEL scripts can use string resources in a similar manner to the C++ code.
To use a string resource, the value is retrieved using the getPluginResource
command. The arguments passed are the two elements of the string resource key.
string $titleStr = getPluginResource("closestPointOnCurve", "kAETitle");
editorTemplate -beginScrollLayout;
editorTemplate -beginLayout $titleStr -collapse 0;
MEL resources are registered using the registerPluginResource
command. The registration process is described below.
String Resource Registration
This section describes the process of registering string resources in the plug-in. All string registration is done during plug-in initialization.
Each string resource must be registered before it can be used. The registration step ensures that the resource's default value is loaded into the string catalog. After the default values are registered the plug-in writer must add a call which will load the localized resource values. When localized resource values are available for the language Maya is running in, the localized values will override the default values.
The main registration method is MFnPlugin::registerUIStrings()
. A call to this routine is added to the plug-in's initializePlugin()
function. It should be placed early in the initialization sequence since the string resources will not be available until it is called, and some of the other initialization methods may require them.
The MFnPlugin::registerUIStrings
function takes two arguments. The first argument is the name of a procedure which will register the strings used in the C++ code. The second argument is the name of a script that will register the string resources used in MEL scripts.
// Register string resources used in the code and scripts
status = plugin.registerUIStrings(registerMStringResources, "closestPointOnCurveInitStrings");
if (!status)
{
status.perror("registerUIStrings");
return status;
}
The C++ routine registerMStringResources
referenced in this call registers each MStringResourceId
used by the C++ code.
// Register all strings used by the plugin C++ source
static MStatus registerMStringResources(void)
{
MStringResource::registerString(kNoValidObject);
// other resources would go here
return MS::kSuccess;
}
This example is using a constant previously defined in a header file:
#define kPluginId "closestPointOnCurve"
#define kNoValidObject MStringResourceId(kPluginId, "kNoValidObject", \"A curve or its transform node must be specified as a command argument, or using your current selection.")
The second argument is the name of a script which initializes all resources used by the plug-in's MEL scripts. Each resource is registered with a call to registerPluginResources
.
The initialization script serves a dual purpose. In addition to the MEL resource registration it also contains the logic to load language-dependent resources for the plug-in. The routine loadPluginLanguageResources
takes the name of a resource file that will contain the localized version of the plug-in string resources. More details creating and installing the localized resource file are found under Localization Process below.
global proc closestPointOnCurveInitStrings()
{
// Register script resources
registerPluginResource("closestPointOnCurve", "kAETitle",
"Closest Point On Curve Attributes");
registerPluginResource("closestPointOnCurve", "kInputCurve",
"Input Curve");
registerPluginResource("closestPointOnCurve", "kResults",
"Results");
// Load any localized resources
loadPluginLanguageResources("closestPointOnCurve", "closestPointOnCurve.pres.mel");
}
Once the registration sequence is complete the strings are available in the Maya Catalog and can be retrieved using MString::getStringResource
(C++) or getPluginResource
(MEL). If localized values for the resources were located by loadPluginLanguageResources
they will be returned from the catalog instead of the default values.
String Handling
Plug-in writers using the MString
class are largely insulated from locale-dependent changes, but some string handling code may require changes to operate correctly in both single and multi-byte environments. This section mainly focuses on issues as they relate to the use of MString
in a localized environment but many of the problems described apply in to character handling in general.
Encoding
The MString
class operates under the assumption that by default, character data in char* form is encoded in the codeset of the locale. This is a natural extension of the existing functionality of the class and in many cases an existing plug-in will continue to work without changes in a localized environment. New methods have been added to explicitly assign or access the string using UTF-8 and wide character formats which are commonly used in internationalized applications.
String Length and Position Values
The most common problem when dealing with localized text is the correct interpretation of string length. In multi-byte environments, the character (char *
) representation of the string will use one or more bytes to represent each character in the string. This means that the string's storage length in bytes does not necessarily correspond to the number of individual characters in the string and code using this assumption may not behave as expected. For backwards compatibility, the MString::length
method will continue to return the number of bytes in the character buffer, a new method MString::numChars
can be used instead when it is necessary to determine the number of individual characters in the string.
The interpretation of positional indexes into the string data is similarly problematic in a multi-byte environment (for example when using the MString::substring
method). See the New MString Methods for details on what has been added to deal correctly with multi-byte strings.
Formatting Message Strings
User message strings are often built by concatenating multiple strings or variables. This technique is not appropriate for strings that will be localized, since the context and placement of the strings may need to change for another language.
The MString::format
method or MEL format
command should be used to format the string. Format allows positional arguments to be correctly placed in context when the string is translated.
The following example shows an original block of code which creates an error message string containing the name of a file using string concatenation:
MString filename;
MString msg;
msg = "The file ";
msg += filename;
msg += " cannot be opened";
displayError(msg);
The following replacement code shows how this would be done correctly in an internationalized application. The message is being created using a resource string and the MString::format
command:
#define kOpenError MStringResourceId("myPlugin", "kOpenError", "File ^1s cannot be opened");
MString filename;
MString msgFmt = MStringResource::getString(kOpenError,status);
MString msg;
msg.format(msgFmt, filename);
displayError(msg);
MEL scripts can make use of the MEL format command in a similar manner.
New MString Methods
The following table lists new methods that have been added to MString
to support internationalization. See the MString
class documentation for more details about each method, as well as notes about the behavior of existing methods within a localized environment.