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:

  1. Determine what aspects of the plug-in will require localization.
  2. 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.
  3. Add string registration calls to the plug-in initialization sequence.
  4. 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:

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.

New MString Method Notes
MString::numChars This routine returns the number of characters in the string. This does not necessarily correspond to the number of bytes in the string, or the value returned by MString::length.

MStatus MString::setUTF8( const char * utf8String )

const char * MString::asUTF8()

const char * MString::asUTF8(int *length)

These methods assign or access the string value as a UTF-8 encoded character string.

Mstring::MString(wchar_t *str)

MString::MString(wchar_ *str, int length)

MStatus MString::setWChar(wchar_t *str, int length)

MStatus MString::setWChar(wchar_t *str, int length)

const wchar_t* MString::asWChar()

const wchar_t* MString::asWChar(int length)

These methods allow the string's value to be set or accessed using wide character values. Note: the use of wide character representation is not recommended for persistent storage, use of a portable format such as UTF-8 is recommended instead.

int MString::indexW(char c) const

int MString::indexW(wchar_t c) const

int MString::rindexW(char c) const

int MString::rindexW(wchar_t c) const

These routines are multi-byte compatible versions of MString::index and MString::rindex respectively and return character-based position values. Use of these routines for internationalized plug-ins is recommended. See also MString::substringW.
MString MString::substringW(int start, int end) This is a multi-byte compatible version of MString::substring which accepts character-based position values (such as those returned by MString::indexW and MString::rindexW).
MStatus MString::split(wchar_t c, MStringArray& array) const This version of MString::split accepts a wide-character value as the delimiter.
MStatus MString::format( const MString &fmt, const MString &arg1, const MString &arg2, ... ,const MString &arg10 ) This routine provides string formatting capabilities, using a format specifier and up to 10 positional arguments.